Noexcept w konstruktorze przenoszącym oraz operatorze przenoszącym

0

Dobry wieczór. Zacząłem czytać o noexcept, że może w pewnych sytuacjach poprawić optymalizację kodu, że warto, itp. Stąd też moje pytanie. Jeśli mam np. klasę zawierającą std::vector, czy mogę z czystym sumieniem zadeklarować konstruktor przenoszący oraz operator przenoszenia jako noexcept? Wg dokumentacji przenoszenie std::vector jest noexcept tylko pod pewnymi warunkami. Jeśli więc mógłbym użyć takiej deklaracji, w jaki sposób zadeklarować "noexcept tylko, gdy jest to możliwe dla std::vector"?

2

Użyj operatora noexcept(). Osobiście domyślnie tego nie robię, bo to zdecydowanie za dużo roboty w porównaniu do przydatności. Piszę z głowy, więc może być nie do końca poprawnie, ale generalnie spodziewaj się czegoś takiego (noexcept(noexcept( to nie typo):

struct foo
{
    foo& operator=(foo&& o) noexcept(noexcept(this->foos = o.foos) && noexcept(this->some = o.some)) = default;
    std::vector<int> foos;
    some_t some;
};

Nie ma czegoś takiego jak noexcept(auto), a w obecnym wyrażeniu musisz generalnie zamieścić całe ciało funkcji, niejako duplikując jej kod (analogicznie do trailing return w type w C++11 przed wprowadzeniem auto return types).

0

Mniej więcej rozumiem, o co chodzi. Czy konstruktor w takiej formie musi być default?

2

Można w tym przypadku użyć specjalnie do tego celu dedykowanych funkcji z biblioteki "type traits".
std::is_nothrow_move_constructible<T>
std::is_nothrow_move_assignable<T>

template< typename T >
class foo
{

    std::vector<T> vect;

public:

    foo() = default;
    foo( foo&& ) noexcept(std::is_nothrow_move_constructible<std::vector<T>>::value) = default;
    foo& operator=(foo&& o) noexcept(std::is_nothrow_move_assignable<std::vector<T>>::value) = default;

};
2

IMO takie coś powinno być obsłużone przez rule of zero, więc nie ma co za bardzo kombinować.

niepamietamloginu napisał(a):

Mniej więcej rozumiem, o co chodzi. Czy konstruktor w takiej formie musi być default?

Nie nie musi. Nigdy nie ma obowiązku używania default. default jest przydatne, kiedy domyślne implementacje metody klasy nie są generowane automatycznie, a domyślna implementacja jest pożądana (np gdy jawnie zdefiniujesz copy constuctor domyślny move constructor i move assignment jest pomijany).

0

Przepraszam za tak długą nieobecność w wątku. Dziękuję wszystkim za pomoc. Czy tak zdefiniowany konstruktor można uznać za poprawny?

template <typename V, typename E>   //konstruktor przenoszący
Graph<V, E>::Graph(Graph<V, E>&& rhs) noexcept(noexcept(vertices=rhs.vertices) && noexcept(edges=rhs.edges))
    : vertices(std::move(rhs.vertices)), edges(std::move(rhs.edges)),
    numberOfVertices(rhs.numberOfVertices), numberOfEdges(rhs.numberOfEdges) {
        rhs.numberOfVertices=0;
        rhs.numberOfEdges=0;
}

Ewentualnie coś takiego:

template <typename V, typename E>   //konstruktor przenoszący
Graph<V, E>::Graph(Graph<V, E>&& rhs) noexcept(noexcept(std::move(rhs.vertices)) && noexcept(std::move(rhs.edges)))
    : vertices(std::move(rhs.vertices)), edges(std::move(rhs.edges)),
    numberOfVertices(rhs.numberOfVertices), numberOfEdges(rhs.numberOfEdges) {
        rhs.numberOfVertices=0;
        rhs.numberOfEdges=0;
}

1 użytkowników online, w tym zalogowanych: 0, gości: 1