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
"?
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).
Mniej więcej rozumiem, o co chodzi. Czy konstruktor w takiej formie musi być default
?
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;
};
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).
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;
}