5

Sto provando a progettare una classe che ha due vettori di grandi sequenze.Come posso creare un costruttore basato su modelli che consenta tutti i valori di riferimento l, valore ref e lista inizializzatore?

std::vector<double> factory() { 
    return std::vector<double>{1,2,3}; // it actually generates a large sequence of double 
} 

struct my_class { 
    my_class(const std::vector<double>& x, const std::vector<double>& y) 
    : m_x(x), m_y(y) 
    { } 

    std::vector<double> m_x; 
    std::vector<double> m_y; 
}; 

int main() { 
    my_class c(factory(), factory()); 
    my_class c2(factory(), {0.5, 1, 1.5}); 
} 

Bene, funziona bene ma non utilizza il costruttore di spostamenti del vettore. Così ho provato a cambiare il costruttore per accettare riferimenti di valore R con inoltro perfetto.

struct my_class { 
    template<typename X, typename Y> 
    my_class(X&& x, Y&& y 
      , typename std::enable_if<std::is_convertible<X, std::vector<double> >::value && 
             std::is_convertible<Y, std::vector<double> >::value>::type * = 0 
      ) 
    : m_x(std::forward<X>(x)), m_y(std::forward<Y>(y)) 
    { } 

    std::vector<double> m_x; 
    std::vector<double> m_y; 
}; 

E ora ho un problema. Quando provo a costruire un'istanza con un initializer_list, ho ricevuto un errore come questo.

$ g++ -W -Wall -std=gnu++0x a.cpp 
a.cpp: In function ‘int main()’: 
a.cpp:34:32: error: no matching function for call to ‘my_class::my_class(std::vector<double>, <brace-enclosed initializer list>)’ 
a.cpp:17:18: note: candidate is: my_class::my_class(const my_class&) 

ho pensato che std::initializer_list<double> potrebbe non essere convertibile in std::vector<double>, ma in realtà è convertibile ed ho ottenuto lo stesso errore quando ho provato senza l'argomento enable_if. Mi sto perdendo qualcosa?

+0

Per curiosità quale versione di g ++ stai usando? IIRC il supporto 'initializer_list' era incompleto in diverse versioni recenti. – Flexo

+0

@awoodland Sto usando gcc 4.6.2. Cosa intendi per "incompleto"? – kukyakya

risposta

7

L'idioma preferito è quello di pass by value e quindi spostare manualmente all'interno della lista dei membri di inizializzazione:

struct my_class { 
    my_class(std::vector<double> x, std::vector<double> y) 
    : m_x(std::move(x)), m_y(std::move(y)) 
    { } 

    std::vector<double> m_x; 
    std::vector<double> m_y; 
}; 

questo funzionerà con tutte le possibili argomentazioni ed essere ragionevolmente veloce:

  • Se si passa un vettore lvalue, il vettore sarà copiato in x e quindi spostato in m_x.
  • Se si passa un vettore rvalue, il vettore sarà spostato in x e poi spostati di nuovo in m_x.
  • Se si passa un elenco di inizializzazione, x verrà inizializzato da tale elenco e quindi spostato in m_x.

L'alternativa è l'inoltro perfetto, ma che rende più difficile per il cliente di sapere che cosa egli può passare:

struct my_class { 
    template<typename T, typename U> 
    my_class(T&& x, U&& y) 
    : m_x(std::forward<T>(x)), m_y(std::forward<U>(y)) 
    { } 

    std::vector<double> m_x; 
    std::vector<double> m_y; 
}; 

Inoltre, ho un sacco di avvertimenti in g ++, così ho wouldn' lo consiglio Basta menzionarlo per completezza.

+0

Il pass-by-value genererà una copia degli argomenti, che è esattamente ciò che user1030861 vuole evitare, se ho capito bene la domanda. – Gabriel

+0

@Gabriel: dipende dalla categoria di valore dell'argomento. Se l'argomento è un valore, call by value * sposta * l'argomento, non copia. Una copia viene eseguita solo se l'argomento è un lvalue. – fredoverflow

+0

@Gabrial: aggiornata la mia risposta con una spiegazione più approfondita. Questo aiuta? – fredoverflow

Problemi correlati