2012-12-30 12 views
5

Supponiamo di avere una sicurezza di classe come segue:Ottenere il tipo di valore di archiviazione appropriato?

struct Base {} 

template<typename T> 
struct Box : Base 
{ 
    template<typename... Args> 
    Box(Args&&... args) 
     : t(forward<Args>(args)...) 
    {} 

    T t; 
} 

E poi abbiamo una funzione di makebox:

template<typename X> 
Base* MakeBox(X&& x) 
{ 
    return new Box<???>(forward<X>(x)); 
} 

Il tipo X è dedotta da parametro utilizzato nella chiamata a makebox.

Abbiamo poi bisogno di calcolare in qualche modo da X appropriato "tipo di archiviazione" parametro T.

Penso che se abbiamo appena ingenuamente utilizziamo:

return new Box<X>(forward<X>(x)); 

allora questo causerà problemi.

Chiaramente std::bind e std::function devono occuparsi di questi problemi, come fanno?

È std::decay utile in ogni caso qui?

+0

Non è ['std :: remove_reference '] (http://en.cppreference.com/w/cpp/types/remove_reference) che cosa stai cercando? – jogojapan

+0

@jogojapan: è ciò che 'std :: bind' usa per memorizzare i suoi parametri associati? –

+3

'std :: decay' è esattamente quello che stai cercando. – Mankarse

risposta

1

Per l'esempio fornito (1) fa ciò che si desidera e memorizza il valore. Per altri casi (ad esempio quando x è un array) è possibile utilizzare std::decay per decaderlo su un puntatore e memorizzarlo.

3

Se ho capito correttamente cosa si desidera ottenere, è necessario utilizzare std::decay. Supponendo di fornire un oggetto di tipo S a MakeBox(), il riferimento universale X&& verrà risolto in modo da rendere l'argomento della funzione di tipo S& o S&& a seconda che l'argomento sia (rispettivamente) un valore di lvalue o un valore di rvalue.

per raggiungere questo obiettivo e per C++ 11 regole per i riferimenti universali, nel primo caso l'argomento modello verrà dedotta come X=S& (qui X non sarebbe OK come argomento per Box<>, perché la vostra variabile membro deve essere un oggetto e non un riferimento oggetto), mentre nel secondo caso sarà dedotto come X=S (qui X andrebbe bene come argomento a Box<>). Applicando std::decay potrete anche applicare implicitamente std::remove_reference al tipo dedotta X prima di alimentarlo come argomento di modello per Box<>, che si sta per fare in modo che X sarà sempre uguale S e mai S& (si prega di tenere a mente, che X è mai sarà dedotto come S&& qui, sarà S o S&).

#include <utility> 
#include <type_traits> 
#include <iostream> 

using namespace std; 

struct Base {}; 

template<typename T> 
struct Box : Base 
{ 
    template<typename... Args> 
    Box(Args&&... args) 
     : t(forward<Args>(args)...) 
    { 
    } 

    T t; 
}; 

template<typename X> 
Base* MakeBox(X&& x) 
{ 
    return new Box<typename decay<X>::type>(forward<X>(x)); 
} 

struct S 
{ 
    S() { cout << "Default constructor" << endl; } 
    S(S const& s) { cout << "Copy constructor" << endl; } 
    S(S&& s) { cout << "Move constructor" << endl; } 
    ~S() { cout << "Destructor" << endl; } 
}; 

S foo() 
{ 
    S s; 
    return s; 
} 

int main() 
{ 
    S s; 

    // Invoking with lvalue, will deduce X=S&, argument will be of type S& 
    MakeBox(s); 

    // Invoking with rvalue, will deduce X=S, argument will be of type S&& 
    MakeBox(foo()); 

    return 0; 
} 

Se siete interessati, ecco un ottimo lezione da Scott Meyers in cui spiega come riferimenti universali si comportano:

Scott Meyers on universal references

PS: Questa risposta è stato modificato: il mio originale risposta suggerita di utilizzare std::remove_reference<>, ma std::decay si è rivelata una scelta migliore.Ringraziamenti alla domanda poster @Andrew Tomazos FathomlingCorps, che lo ha sottolineato, e a @Mankarse, che per primo lo ha proposto in un commento alla domanda originale.

+0

Che dire di 'std :: decay'? –

+0

Infatti 'std :: decay' sembra essere una buona opzione, probabilmente ancora meglio perché include anche' std :: remove_reference <> '. Il motivo per cui non l'ho considerato all'inizio è che toglie il qualificatore 'const', ma poi mi sono reso conto che probabilmente non vuoi affatto mantenere il qualificatore' const', dato che stai creando un nuovo oggetto all'interno di 'Box' e chiamando il suo costruttore di copie. Se è così, possiamo davvero sostituire 'std :: remove_reference' con' std :: decay' nella mia risposta. Lo farò e darò il giusto credito. –

+0

Btw, riguardo alla funzione 'MakeBox <>()' e alla classe 'Box <>', sei sicuro di voler chiamare sempre solo il costruttore di copia di 'T' tutto il tempo? Avete un pacchetto di argomenti nel costruttore di 'Box <>' per inoltrare gli argomenti al costruttore di 'T', ma passate sempre un solo oggetto' T' in input, quindi invoca sempre il costruttore di copie. Non dovresti avere una lista variadica anche in 'MakeBox <>()'? Btw, invece di restituire una Base * restituirei il puntatore esatto ('Box :: type> *') –

Problemi correlati