7

tengo la intención de utilizar shared_ptr un poco en un próximo proyecto, por lo que (no ser consciente de std::make_shared) que quería escribir una función de plantilla variadic spnew<T>(...) como shared_ptr -Volviendo en stand-para new . Todo fue bien hasta que intenté usar un tipo cuyo constructor incluye un initializer_list. Me sale el siguiente GCC 4.5.2 desde cuando intento compilar el ejemplo mínima a continuación:La expansión paquete de parámetro que contiene initializer_list al constructor

 
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()' 

Por extraño que parezca, tengo errores equivalentes si sustituyo std::make_shared para spnew. En cualquier caso, parece deducir los parámetros incorrectamente cuando se trata de initializer_list, tratando erróneamente Args... como vacío. Aquí está el ejemplo:

#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"}); 
} 

¿Esto es solo un descuido de mi parte o un error?

+1

Hay 'std :: make_shared', por cierto. – GManNickG

+0

@GMan: Sí, lo he encontrado y lo usaré, pero todavía tengo curiosidad por saber qué hay con lo que escribí. –

+0

@GMan: en realidad, vengo a intentar sustituir 'make_shared' por' spnew' en mi ejemplo, aún falla para el caso 'failed' con errores equivalentes. Así que ahora al menos sé dónde no está el error ... –

Respuesta

0

con gcc-4.7 (probablemente trabajaría en gcc-4.6 también, sólo ramificado) con las advertencias:

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] 

No estoy seguro de por qué alguien querría para reforzar sobre la deducción lista init sin embargo.

hay un hilo relacionado: Why doesn't my template accept an initializer list

Básicamente, una lista de init desnudo No tiene un tipo.

+1

En cuanto a la carne de vacuno, el comportamiento de deducción de la lista de inicio predeterminada es una extensión especulativa anterior que puede entrar en conflicto con una propuesta más reciente, por lo que '-fno-deduce-init-list' se creó. –

1

Usted puede hacer esto -

#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; 
} 

Esto, a pesar de las plantillas genéricas es escribir seguro como el compilador se quejará de la contents.push_back si el tipo pasado no se puede convertir a un const char *.

Como se describió anteriormente, el código estaba trabajando bien con gcc 4.6 sin embargo, la advertencia que recibe se explica aquí why-doesnt-my-template-accept-an-initializer-list, y es posible que no cumplen con las normas , aunque la norma ++ 0x c aún no se ha publicado por lo que este podría cambio.

Cuestiones relacionadas