5

Sto cercando di creare una cosa simile alla tupla, ma ho incontrato un problema per scrivere il mio costruttore.Come creare un costruttore di inoltro perfetto per una classe variadica simile a una tupla

Ecco il codice:

#include <tuple> 

template <typename... Ts> 
struct B { 
    template <typename... ArgTypes> 
    explicit B(ArgTypes&&... args) 
    { 
     static_assert(sizeof...(Ts) == sizeof...(ArgTypes), 
      "Number of arguments does not match."); 
    } 
}; 

struct MyType { 
    MyType() = delete; 
    MyType(int x, const char* y) {} 
}; 

int main() 
{ 
    B   <int, char>    a{2, 'c'};      // works 
    B   <int, bool, MyType, char> b{2, false, {4, "blub"}, 'c'}; // fails 
    std::tuple<int, bool, MyType, char> t{2, false, {4, "blub"}, 'c'}; // works 
} 

Ora, questo funziona bene se passare tipi semplici come inizializzatori, ma non lo fa, se provo a passare gli argomenti in una lista di inizializzazione brace-chiusa per non banale oggetto.

GCC-4.7 emette il seguente:

vararg_constr.cpp:21:67: error: no matching function for call to 'B<int, bool, MyType, char>::B(<brace-enclosed initializer list>)' 
vararg_constr.cpp:21:67: note: candidates are: 
vararg_constr.cpp:6:14: note: B<Ts>::B(ArgTypes&& ...) [with ArgTypes = {}; Ts = {int, bool, MyType, char}] 
vararg_constr.cpp:6:14: note: candidate expects 0 arguments, 4 provided 

Clang-3.1 il seguente:

vararg_constr.cpp:21:40: error: no matching constructor for initialization of 
     'B<int, bool, MyType, char>' 
    B   <int, bool, MyType, char> b{2, false,{4, "blub"}, 'c'}; // fails 
             ^~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
vararg_constr.cpp:6:14: note: candidate constructor not viable: requires 2 
     arguments, but 4 were provided 
    explicit B(ArgTypes&&... args) 

Ok, ora ciò che mi rende molto, molto curioso è che funziona per tupla! Secondo lo Standard (20.4.2.1) ha un costruttore, che assomiglia molto al mio.

template <class... Types> 
class tuple { 
public: 
    // ... 

    template <class... UTypes> 
    explicit tuple(UTypes&&...); 

    // ... 
}; 

Quando si costruisce l'oggetto tupla allo stesso modo, funziona!

Ora vorrei sapere:

A) Che diavolo? Perché std :: tuple è così speciale, e perché i compilatori non deducono il numero corretto di argomenti?

B) Come posso fare questo lavoro?

risposta

6

A) Perché il compilatore dovrebbe sapere che {4, "blub"} è di tipo MyType e non tuple<int, const char*>?

B) Variazione ArgTypes a Ts nel costruttore:

explicit B(Ts&&... args) 

tuple non avere anche il seguente costruttore:

explicit constexpr tuple(const _Elements&... __elements); 

EDIT: Il punto è che il costruttore con const & viene chiamato e non quello con i valori R. Considerare quanto segue:

template <typename... Ts> 
struct B { 
    explicit B(const Ts&... elements) { std::cout << "A\n"; } 
    template<typename... As, 
      typename = typename std::enable_if<sizeof...(As) == sizeof...(Ts)>::type> 
    explicit B(As&&... elements) { std::cout << "B\n" ;} 
}; 

int main() 
{ 
    MyType m {1, "blub"}; 
    B<int, char>   a{2, 'c'};       // prints B 
    B<bool, MyType, char> b{false, {4, "blub"}, 'c'};   // prints A 
    B<bool, MyType, MyType>c{false, {4, "blub"}, std::move(m)}; // prints A 
} 
+0

A) Come fa il compilatore a sapere per il costruttore tupla che si tratta di una tupla e non di MyType? B) Scusa, avrei dovuto elaborare una tupla un po '. Tuple è parametrizzato su Ts ..., ma il costruttore di cui sto parlando è inoltre parametrizzato su UTypes .... –

+0

Ho modificato per rendere il problema A) un po 'più chiaro – ipc

+1

Si noti che per la parte A, ponendo '&&' sui parametri del modello che racchiude il significato, quindi non è più l'inoltro perfetto, ma lo farà (escludendo gli argomenti di riferimento a ' Ts') formano riferimenti di valore, che non sono desiderati. Per la parte B, i riferimenti rvalue sono "corretti" su lvalue 'const &', quindi di solito funziona, ma non è ancora l'inoltro perfetto. – Potatoswatter

Problemi correlati