2013-06-03 21 views
12

Kod:Move generacja funkcji członka

#include <iostream> 
#include <ios> 
#include <string> 
#include <type_traits> 
#include <memory> 

struct value 
{ 
    ~value() = default; 
    std::unique_ptr<std::string> s; 
}; 

int main() 
{ 
    std::cout << std::boolalpha; 
    std::cout << std::is_move_constructible<value>::value << '\n'; 
    std::cout << std::is_move_assignable<value>::value << '\n'; 

    using str_ptr = std::unique_ptr<std::string>; 
    std::cout << std::is_move_constructible<str_ptr>::value << '\n'; 
    std::cout << std::is_move_assignable<str_ptr>::value << '\n'; 

    return 0; 
} 

wyjściowa (skompilowane z g ++ v4.7.2, http://ideone.com/CkW1tG):

 
false 
false 
true 
true 

jak oczekuję, value nie poruszać constructible i nie przenieść przypisane ponieważ :

~value() = default; 

jest deklarowanym przez użytkownika destruktor, który zapobiega niejawnej generacji elementów ruchu zgodnie z sekcją 12.8 (patrz poniżej). Po usunięciu destruktora, value jest ruchomy i można go przenosić, tak jak się spodziewam (http://ideone.com/VcR2eq).

Jednak, gdy definicja value zmienia się na (http://ideone.com/M8LHEA):

struct value 
{ 
    ~value() = default; 
    std::string s;  // std::unique_ptr<> removed 
}; 

wyjście jest:

 
true 
true 
true 
true 

value niespodziewanie ruszyć constructible i przenieść przypisane. Czy nie rozumiem, czy jest to błąd kompilatora?


Tło: I dały odpowiedź na pytanie this i został poinformowany, że Tree<> był ruchomy, ale jestem pewien, i 'm próbuje ustalić na pewno, czy jest, czy nie.


Sekcja 8.4.2 Jawnie-zaległych funkcje o standard C++ 11 (Projekt n3337):

Jawnie-zaległych funkcje i niejawnie-zadeklarowane funkcje zwanych zaległych funkcje , a implementacja musi zawierać domyślne definicje (12.1 12.4, 12.8), co może oznaczać definiowanie ich jako usuniętych. Specjalna funkcja składowa jest dostarczana przez użytkownika, jeśli została zadeklarowana przez użytkownika, a jawnie niewpisana domyślnie lub usunięta przy pierwszej deklaracji. Podana przez użytkownika jawnie domyślna funkcja (tj. Jawnie domyślnie po pierwszej deklaracji) jest zdefiniowana w punkcie, w którym jest ona domyślnie ustawiona na ; jeśli taka funkcja jest niejawnie zdefiniowana jako usunięta, program jest źle sformułowany. [Uwaga: Deklarowanie funkcji jako domyślnie po pierwszej deklaracji może zapewnić skuteczną realizację i zwięzłą definicję podczas umożliwiając stabilną interfejs binarny do rozwijającej kod base.-end Uwaga]

rozdział 12.8 kopiowania i przenoszenia klasa obiektów (punkt 9)

 
If the definition of a class X does not explicitly declare a move constructor, 
one will be implicitly declared as defaulted if and only if 
- X does not have a user-declared copy constructor, 
- X does not have a user-declared copy assignment operator, 
- X does not have a user-declared move assignment operator, 
- X does not have a user-declared destructor, and 
- the move constructor would not be implicitly defined as deleted. 

Odpowiedz

9

std::is_move_constructible<T> prawda IFF std::is_constructible<T, T&&> jest, lecz nie oznacza to, że taka konstrukcja będzie wywoływać konstruktora ruch tylko to, że możliwe jest skonstruowanie typ od wartości tego samego typu. Taka konstrukcja może używać konstruktora kopii.

Kiedy value::s jest unique_ptr, konstruktor kopiowania typu i operator przypisania kopiowania są zdefiniowani jako usunięci, ponieważ element s nie może być kopiowany. Nie ma konstruktora ruchu i operatora przeniesienia, ponieważ, jak wskazałeś, ma destruktora deklarowanego przez użytkownika. Oznacza to, że nie ma konstruktora kopiowania ani konstruktora ruchu (i nie ma innych zdefiniowanych przez użytkownika konstruktorów, które mogłyby zaakceptować argument typu value&&), więc std::is_constructible<value, value&&> jest fałszywe.

Gdy value::s jest string konstruktor kopii na typ i kopiowania operator przypisania są nie zdefiniowane jako usunięte, ponieważ element s jest copyable itd value jest copyable i rodzaj CopyConstructible jest MoveConstructible, ponieważ jest to ważne w tym kontekście:

value v1; 
value v2 = std::move(v1); // calls copy constructor 

oznacza to std::is_constructible<value, value&&> jest prawdą, mimo że nie wywołuje konstruktor kopiujący konstruktora pojedynek.

+0

+1 Dzięki, bardzo jasne wyjaśnienie. Czy jesteś ciekaw, czy znasz jakąś cechę, która wyraźnie sprawdza obecność członków ruchu? – hmjd

+1

Nie ma ani jednego. [_ "semantyka języka rdzeniowego nie jest zbyt użyteczna dla pisarza biblioteki, nie obchodzi go, czy klasa ma coś, co język podstawowy uważa za konstruktor kopii, chcą wiedzieć, czy faktycznie pisze T (someT) zadziała, bez względu na to, czy wywoła konstruktor kopiowania, konstruktor ruchu, konstruktor szablonów lub jakikolwiek konstruktor. "_] (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2010/ n3142.html) Można jednak sprawdzić wartość 'is_move_constructible :: value &&! is_copy_constructible :: value' –

Powiązane problemy