7

ho intenzione di usare shared_ptr un bel po 'in un progetto imminente, quindi (non essere a conoscenza di std::make_shared) Volevo scrivere una funzione template variadic spnew<T>(...) come shared_ptr -returning stand-in per new . Tutto è andato liscio fino a quando ho tentato di utilizzare un tipo il cui costruttore include uno initializer_list. Ottengo il seguente da GCC 4.5.2, quando provo a compilare l'esempio minimo di seguito:Espansione parametro confezione contenente initializer_list al costruttore

 
In function 'int main(int, char**)': 
too many arguments to function 'std::shared_ptr spnew(Args ...) [with T = Example, Args = {}]' 

In function 'std::shared_ptr spnew(Args ...) [with T = Example, Args = {}]': 
no matching function for call to 'Example::Example()' 

Stranamente, ottengo errori equivalenti se sostituisco std::make_shared per spnew. In entrambi i casi, sembra che i parametri vengano dedotti erroneamente quando è coinvolto un initializer_list, trattando erroneamente Args... come vuoto. Ecco l'esempio:

#include <memory> 
#include <string> 
#include <vector> 

struct Example { 

    // This constructor plays nice. 
    Example(const char* t, const char* c) : 
     title(t), contents(1, c) {} 

    // This one does not. 
    Example(const char* t, std::initializer_list<const char*> c) : 
     title(t), contents(c.begin(), c.end()) {} 

    std::string title; 
    std::vector<std::string> contents; 

}; 

// This ought to be trivial. 
template<class T, class... Args> 
std::shared_ptr<T> spnew(Args... args) { 
    return std::shared_ptr<T>(new T(args...)); 
} 

// And here are the test cases, which don't interfere with one another. 
int main(int argc, char** argv) { 
    auto succeeds = spnew<Example>("foo", "bar"); 
    auto fails = spnew<Example>("foo", {"bar"}); 
} 

Questa è solo una svista da parte mia o un bug?

+1

C'è 'std :: make_shared', a proposito. – GManNickG

+0

@GMan: Sì, l'ho trovato e lo userò, ma sono ancora curioso di sapere cosa succede con ciò che ho scritto. –

+0

@GMan: In realtà, vieni a provare a sostituire 'make_shared' per' spnew' nel mio esempio, non riesce ancora per il caso 'fallisce' con errori equivalenti. Quindi ora almeno so dove l'errore non è ... –

risposta

0

con gcc-4.7 (probabilmente sarebbe lavorare su gcc-4.6 anche, appena ramificato) con avvisi:

foo.cpp: In function ‘int main(int, char**)’: 
foo.cpp:29:47: warning: deducing ‘Args ...’ as ‘std::initializer_list<const 
char*>’ [enabled by default] 
foo.cpp:22:20: warning: in call to ‘std::shared_ptr<_Tp1> spnew(Args ...) 
[with T = Example, Args = {const char*, std::initializer_list<const 
char*>}]’ [enabled by default] 
foo.cpp:29:47: warning: (you can disable this with -fno-deduce-init-list) 
[enabled by default] 

Io non so perché qualcuno dovrebbe voler rinforzare su init-list detrazione però.

C'è un filo corrispondenti Why doesn't my template accept an initializer list

Fondamentalmente, un nudo init-elenco non ha un tipo.

+1

Come per il manzo, il comportamento predefinito di deduzione della lista di inizializzazione è un'estensione speculativa più vecchia che potrebbe entrare in conflitto con una proposta più recente, motivo per cui '-fno-deduce-init-list' ha avuto origine. –

1

Si potrebbe fare questo -

#include <memory> 
#include <string> 
#include <iostream> 
#include <vector> 

struct Example { 

    template<class... Args> 
    Example(const char* t, Args... tail) : title(t) 
    { 
     Build(tail...); 
    } 

    template<class T, class... Args> 
    void Build(T head, Args... tail) 
    { 
     contents.push_back(std::string(head)); 
     Build(tail...); 
    } 

    template<class T> 
    void Build(T head) 
    { 
     contents.push_back(std::string(head)); 
    } 

    void Build() {}   

    std::string title; 
    std::vector<std::string> contents; 

}; 

template<class T, class... Args> 
std::shared_ptr<T> spnew(Args... args) { 
    return std::shared_ptr<T>(new T(args...)); 
} 

int main(int argc, char** argv) { 
    auto succeeds = spnew<Example>("foo", "bar"); 
    auto fails = spnew<Example>("foo", "bar", "poo", "doo"); 

    std::cout << "succeeds->contents contains..." << std::endl; 
    for (auto s : succeeds->contents) std::cout << s << std::endl; 

    std::cout << std::endl << "fails->contents contains..." << std::endl; 
    for (auto s : fails->contents) std::cout << s << std::endl; 
} 

Questo, nonostante i modelli generici È SICURO tipo come il compilatore lamentano il contents.push_back se il tipo approvato non è convertibile in un const char *.

Come descritto sopra, il codice stava lavorando bene con gcc 4.6 tuttavia l'avviso che si ottiene è spiegato qui why-doesnt-my-template-accept-an-initializer-list, ed è forse non standard compliant, anche se il C++ 0x standard è ancora essere pubblicato in modo da questo potrebbe modificare.