2010-01-26 17 views
5

¿Cómo es que no puedo instanciar un objeto de tipo Foo con el constructor anterior?El constructor de plantillas C++ no compilará

Tengo una barra de clase que usa un typedef interno (como una solución para "template typedefs") y tengo la intención de usarlo en un constructor como el siguiente (CASE 1). Sin embargo, parece que no lo compilo. ¿Es esto legal C++? CASO 2 parece sugerir que el problema está relacionado con el typedef en Bar.

¿Cómo puedo definir un constructor que acepte std :: vectores de objetos con el tipo en Barra?

#include <vector> 
#include <iostream> 
#include <utility> 

template <typename T> 
struct Bar 
{ 
    typedef std::pair<T, T> type; // or anything else that uses T 
}; 

struct Foo 
{ 
    Foo() {} 

    // CASE 1: doesn't compile 
    template <typename T> explicit Foo(const std::vector<typename Bar<T>::type>& data) 
    { 
     std::cout << "Hello\n"; 
    } 

    //// CASE 2: compiles, but it's not what I want 
    //template <typename T> explicit Foo(const std::vector<Bar<T> >& data) 
    //{ 
    // std::cout << "Hello\n"; 
    //} 
}; 

int main() 
{ 
    std::vector<Bar<int>::type> v; // for CASE 1 
    //std::vector<Bar<int> > v; // for CASE 2 

    Foo f(v); 
    return 0; 
} 
+0

¿Se puede publicar el error de salida? –

+0

VC++ confunde mi constructor con el constructor de copia y escribe: 'Foo :: Foo (const Foo Y)': no ​​se puede convertir el parámetro 1 de 'std :: vector <_Ty>' a 'const Foo &' salidas del CCG: no hay función de coincidencia para la llamada a 'Foo :: Foo (std :: vector > &)' – kmhofmann

+1

El compilador Intel C++ da la siguiente advertencia útil en la línea con la declaración del constructor: advertencia # 488 : el parámetro de plantilla "' T' "no se utiliza para declarar los tipos de parámetros de la plantilla de función" 'Foo :: Foo (const std :: vector :: type, std :: allocator :: type >> &)' " –

Respuesta

9

Según el párrafo 14.8.2.1 de la norma el C++, cuando un parámetro de plantilla se usa sólo en un contexto no deducido, el argumento de plantilla correspondiente no puede deducirse:

Si una plantilla-parámetrono se utiliza en ninguno de los parámetros de función de una plantilla de función, o se usa solo en un contexto no deducido, su argumento de plantilla correspondiente no se puede deducir de una llamada a función y el argumento de plantilla debe especificarse explícitamente .

La definición de contextos nondeduced, como se indica en §14.8.2.4:

Los contextos nondeduced son:

  • la nombre-nested-especificador de un tipo que se especificó utilizando un id-calificado.

  • un tipo que es un plantilla-id en wich uno o más de los plantilla-argumentos es una expresión que hace referencia a un plantilla-parámetro.

En Bar<T>::type, Bar<T> es un nombre -anidado-especificador y por lo tanto un contexto no deducido, por lo que debe especificar explícitamente el argumento de plantilla cuando una llamada al constructor ... lo cual no es posible (es decir, no puede escribir Foo f<int>(v)).

supongo que el compilador no puede deducir el argumento de plantilla porque eso sería al menos engorroso y más probablemente imposible: imaginar Bar es especializada:

template<typename T> 
struct Bar 
{ 
    typedef std::pair<T,T> type; 
}; 

template<> 
struct Bar<char> 
{ 
    typedef std::pair<int,int> type; 
}; 

Ahora tienen una ambigüedad cuando se llama al constructor de Foo con std::vector<std::pair<int,int> >: ¿El argumento de la plantilla debe ser int o char? E incluso si no existiera tal ambigüedad, puede ver fácilmente que el compilador tendría que crear una instancia de Bar con potencialmente cualquier tipo antes de encontrar la instanciación con el typedef correcto (bueno, no estoy tan seguro de que las declaraciones anteriores sean realmente relevantes ya que a menudo descubro que los compiladores son mucho más inteligentes de lo que pensé :-)!)

+0

También puede citar 14.8.2.4/4, que nombra los dos contextos no dictados. –

+0

Gracias por el puntero, agregué la información. –

4

Su constructor es:

template <typename T> 
explicit 
Foo(const std::vector<typename Bar<T>::type>& data) 

La plantilla argumento T no puede deducirse del argumento de la función de esta manera. (Creo que esto se llama un "contexto no deducible", pero no estoy seguro).

Esto simplemente no va a funcionar. Vas a tener que escribir

template <typename B> 
explicit 
Foo(const std::vector<B>& data) 

lugar y encontrar otras maneras para afirmar que B es de tipo typename Bar<T>::type.

+0

Ah, Luc ha proporcionado amablemente capítulo y verso a mi sta bastante vaga tement: http://stackoverflow.com/questions/2140025/c-templated-constructor-wont-compile/2140182#2140182 – sbi

0

Creo que la única razón para eso es que quiere crear una instancia de una barra del tipo correspondiente en lugar de imprimir "hola".

Tal vez usted podría intentar mapear los tipos en la dirección inversa (el tipo de deducción invertido que usted está esperando el compilador sería capaz de interpretar):

#include <utility> 
#include <vector> 

template <class T> 
struct BarType; 

template <class T> 
struct BarType<std::pair<T, T> > 
{ 
    typedef T type; 
}; 

template <class T> 
struct Bar {}; 

struct Foo 
{ 
    template <class T> 
    Foo(const std::vector<T>&) 
    { 
     Bar<typename BarType<T>::type> bar; 
     //... 
    } 
}; 

int main() 
{ 
    Foo(std::vector<std::pair<int, int> >()); 
} 
0

Podría algo así como este trabajo para usted?

#include <vector> 
#include <iostream> 
#include <utility> 
#include <boost/static_assert.hpp> 

template <typename T> 
struct Bar 
{ 
    typedef std::pair<T, T> type; // or anything else that uses T 
    enum {val = 42}; 
}; 

template <typename T> 
struct Traits 
{ 
    enum {allowed = false}; 
}; 

template <typename T> 
struct Traits<std::pair<T, T> > 
{ 
    enum {allowed = true}; 
    typedef Bar<T> BarType; 
}; 

struct Foo 
{ 
    Foo() {} 

    template <typename T> explicit Foo(const std::vector<T>& data) 
    { 
     BOOST_STATIC_ASSERT(Traits<T>::allowed); 
     typedef typename Traits<T>::BarType BarType; 
     std::cout << BarType::val << std::endl; 
    } 
}; 

int main() 
{ 
    std::vector<Bar<int>::type> v; 
    std::vector<float> v2; 

    Foo f(v); 
// Foo f2(v2); // Compile error 
    return 0; 
} 

Esto compila y funciona en GCC 4.4.1. Puede especializar Traits para otros vector::value_type que sean permitidos por su constructor.

Cuestiones relacionadas