2016-06-15 32 views
6

Sto imparando come usare condizionale noexcept e imbattersi in questo problema. Supponiamo che io sono una classe:noexcept espressione vs tipo tratti

template<typename T> 
class Wrapper { 
public: 
    Wrapper(T&& value) noexcept(/* ??? */) 
     : value_(std::move(value)) 
    {} 

private: 
    T value_; 
}; 

Per la parte /* ??? */, ho pensato che possiamo usare sia noexcept(T(std::move(value))) o std::is_nothrow_move_constructible<T>::value, fino a quando sono incappato in this.

Quindi, se io uso noexcept(noexcept(T(std::move(value)))), a rigor di termini che sto dicendo che "questo costruttore è noexcept se e solo se la costruzione e distruggendo un T è noexcept"?

Anche se i distruttori che lanciano devono essere messi a fuoco e bruciati.

+3

È possibile aggirare il problema utilizzando ad es. 'noexcept (new T (std :: move (value)))' o qualcosa del genere? Dal momento che si tratta di un'espressione non valutata, in realtà non sta allocando nulla, ma dovrebbe anche "filtrare" in modo specifico e quindi il dtor non dovrebbe essere coinvolto ... Suppongo che potrebbe essere necessario usare una versione no-throw del nuovo però, dato che non lo fai t voglio rilevare 'std :: bad_alloc'. –

risposta

6

Buona domanda, vedere anche this language defect discussion. Dal suo nome appare chiaro che std::is_nothrow_move_constructible<T>::valuedovrebbe riferirsi solo alla costruibilità da un valore massimo (ma in pratica può anche riferirsi alla distruzione), mentre noexcept(T(std::move(value))) si riferisce sempre sia alla costruzione che alla distruzione.

Quindi, nel tuo caso, il più risparmiare strada, evitando la questione irrisolta dei std::is_nothrow_move_constructible tratti, è quello di utilizzare nuova collocazione, evitando il problema con std::bad_alloc (menzionato nel commento di Chris Beck), e, allo stesso modo, utilizzare T ' s desctructor per il distruttore del wrapper.

template<typename T> 
class Wrapper { 
public: 
    Wrapper(T&& value) noexcept(new(nullptr) T(std::move(value))) 
     : value_(std::move(value)) 
    {} 
    ~Wrapper() noexcept(noexcept(value_.T::~T())) 
    {} 
private: 
    T value_; 
}; 
+1

_ "è chiaro che' std :: is_nothrow_move_constructible :: value' dovrebbe riguardare solo il costruttore "_ - non è chiaro, il problema è irrisolto, e non è certamente vero che vi è un chiaro consenso sul fatto che il l'aspettativa del mittente è corretta, o che ci sia qualche difetto nello standard. –

+0

@JonathanSempre punto preso. Ho modificato la domanda. Penserei che un costrutto standard C++ dovrebbe fare quello che dice sullo stagno, cioè in questo caso verifica solo se un tipo ha un costruttore di move no-throw, ma nient'altro. – Walter

+0

Ma il tratto non è "has nothrow move constructor" (vedi [N3142] (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2010/n3142.html) per il motivo per cui non è quello). Invece ti dice se la creazione di un oggetto da un valore rvalore può essere lanciata, che non è la stessa cosa. E per la maggior parte dei casi in cui vuoi sapere se crearla, devi anche sapere se la distruzione lo getta, perché non è corretto ottimizzare qualcosa come l'interno di un contenitore per elementi con mosse non di lancio se i loro distruttori possono lanciare. –