2011-04-14 22 views
23

Considere la siguiente clase, con la estructura interna Y que se utiliza como un tipo, por ejemplo. en las plantillas, más tarde:Inclusión/exclusión en tiempo de compilación condicional basada en argumento (s) de plantilla?

template<int I> 
class X{ 
    template<class T1> 
    struct Y{}; 

    template<class T1, class T2> 
    struct Y{}; 
}; 

Ahora, en este ejemplo, obviamente, no se compile, con el error de que el segundo X<I>::Y ya se ha definido o que tiene demasiados parámetros de plantilla.
Me gustaría resolver eso sin especialización parcial (extra), ya que el parámetro int I no es el único y la posición de este puede diferir en diferentes especializaciones parciales (mi estructura real se ve more like this, lo anterior es solo por simplicidad de la pregunta), así que me gustaría one class fits every I solución.


Mi primer pensamiento fue, obviamente, enable_if, pero que parece fallar en mí, por ejemplo. Sigo teniendo los mismos errores:

// assuming C++11 support, else use boost 
#include <type_traits> 

template<int I> 
class X{ 
    template<class T1, class = std::enable_if<I==1>::type> 
    struct Y{}; 

    template<class T1, class T2, class = std::enable_if<I==2>::type> 
    struct Y{}; 
}; 

lo tanto, ya enable_if falla, espero que no haya otra manera de alcanzar la siguiente comprobación en tiempo de compilación:

template<int I> 
class X{ 
    __include_if(I == 1){ 
    template<class T1> 
    struct Y{}; 
    } 

    __include_if(I == 2){ 
    template<class T1, class T2> 
    struct Y{}; 
    } 
}; 

sólo habría ser para salvarme mucho de duplicación de código, pero estaría muy feliz si fuera de alguna manera posible.
Editar: Tristemente, no puedo usar lo obvio: plantillas variadic, ya que estoy usando Visual Studio 2010, así que solo puedo usar las cosas de C++ 0x que hay allí. :/

+0

+1. Interesante pregunta. Trataré de responderlo después del horario de oficina: D – Nawaz

+0

Lo estoy esperando con placer. :) Mi línea de pensamiento es que debería ser posible ya que el compilador sabe todo lo que necesita saber, bueno, tiempo de compilación. – Xeo

+0

@Xeo: ¿Se te permite usar C++ 0x feautures? – Nawaz

Respuesta

8

Hay dos problemas aquí:

  1. enable_if obras con la especialización parcial, las plantillas no primarios.
  2. El número de argumentos visible externamente está determinado por la plantilla principal, de la cual puede haber solo una.

respuesta 1.

como usted sugiere en el chat, una lista enlazada de plantillas puede emular el paquete de parámetro variadic.

template<int I> 
class X{ 
    template<class list, class = void> 
    struct Y; 

    template<class list> 
    struct Y< list, typename std::enable_if<I==1>::type > { 
     typedef typename list::type t1; 
    }; 

    template<class list> 
    struct Y< list, typename std::enable_if<I==2>::type > { 
     typedef typename list::type t1; 
     typedef typename list::next::type t2; 
    }; 
}; 

Si usted termina con next::next::next basura, es fácil escribir un metafunción, o utilizar MPL Boost.


respuesta 2.

Las plantillas de diferentes Arity se puede nombrar de manera similar, pero aún quedan distinta si se anidan dentro del tipo controlado-SFINAE.

template<int I> 
class X{ 
    template<typename = void, typename = void> 
    struct Z; 

    template<typename v> 
    struct Z< v, typename std::enable_if<I==1>::type > { 
     template<class T1> 
     struct Y{}; 
    }; 

    template<typename v> 
    struct Z< v, typename std::enable_if<I==2>::type > { 
     template<class T1, class T2> 
     struct Y{}; 
    }; 
}; 

X<1>::Z<>::Y<int> a; 
X<2>::Z<>::Y< char, double > b; 
+0

Sir puede explicar su línea "enable_if funciona con especialización parcial, no plantillas primarias". , ¿por qué no se puede usar con la plantilla primaria como lo hizo OP? Muchas gracias :) –

+1

@AngelusMortis Se deducen los parámetros de la plantilla de especialización parcial (§14.5.5.1 [temp.class.spec.match]). 'enable_if' funciona allí porque la falla de sustitución no es un error (SFINAE) dentro de la deducción. Las plantillas primarias no tienen ese beneficio: la falla de sustitución es un error. – Potatoswatter

0

¿Qué tal este acercamiento - http://sergey-miryanov.blogspot.com/2009/03/template-class-overriding.html? (Lo siento por el ruso)

+0

Lamentablemente no es aplicable, ya que la estructura de plantilla interna se debe usar como un tipo, por ejemplo, en plantillas. :/ – Xeo

+0

Por cierto, [aquí está el sitio traducido por google] (http://translate.google.de/translate?js=n&prev=_t&hl=de&ie=UTF-8&layout=2&eotf=1&sl=ru&tl=en&u=http%3A % 2F% 2Fsergey-miryanov.blogspot.com% 2F2009% 2F03% 2Ftemplate-class-overriding.html & act = url), lo que hace que la intención sea buena. – Xeo

+0

Pensándolo bien, tal vez eso funcionaría con algún truco de decltype. Lo intentaré una vez que llegue a casa. :) – Xeo

1

Se puede tratar a continuación (que no es la especialización parcial):

template<int I> 
class X 
{ 
}; 

template<> 
class X<1> 
{ 
    template<class T1> 
    struct Y{}; 
}; 

template<> 
class X<2> 
{ 
    template<class T1, class T2> 
    struct Y{}; 
}; 

Dudo si la respuesta es así de simple !!

Editar (Burlándose de la especialización parcial): @Xeo, yo era capaz de compilar código siguiente y parece estar fullfilling.

template<int I> 
struct X 
{ 
    struct Unused {}; // this mocking structure will never be used 

    template<class T1, class T2 = Unused> // if 2 params passed-->ok; else default='Unused' 
    struct Y{}; 

    template<class T1> 
    struct Y<T1, Unused>{}; // This is specialization of above, define it your way 
}; 

int main() 
{ 
    X<1>::Y<int> o1; // Y<T1 = int, T2 = Unused> called 
    X<2>::Y<int, float> o2; // Y<T1 = int, T2 = float> called 
} 

Aquí, sin embargo se puede utilizar X < 1>, X < 2> indistintamente. Pero en el ejemplo más amplio que mencionaste, eso es irrelevante. Aún así, si lo necesita, puede colocar cheques para I = 1 y I = 2.

+0

Eso es lo que quise decir con "sin especialización parcial (extra)", ya que el 'int I' no es el único parámetro de plantilla, pero hay otros. Me puse en contacto con Ideone para ver un pequeño ejemplo de lo que realmente se ve, la estructura fue simplificada por la simplicidad de la pregunta. – Xeo

+0

¿Puedes verificar la versión editada? De hecho, he puesto la versión de la segunda plantilla como versión principal y la primera es la especialización de la segunda. ¡Espero eso ayude! – iammilind

3

Aquí van:

http://ideone.com/AdEfl

Y el código:

#include <iostream> 

template <int I> 
struct Traits 
{ 
    struct inner{}; 
}; 

template <> 
struct Traits<1> 
{ 
    struct inner{ 
    template<class T1> 
    struct impl{ 
     impl() { std::cout << "impl<T1>" << std::endl; } 
    }; 
    }; 
}; 

template <> 
struct Traits<2> 
{ 
    struct inner{ 
    template<class T1, class T2> 
    struct impl{ 
     impl() { std::cout << "impl<T1, T2>" << std::endl; } 
    }; 
    }; 
}; 

template<class T> 
struct Test{}; 

template<class T, class K> 
struct Foo{}; 

template<int I> 
struct arg{}; 

template< 
    template<class, class> class T, 
    class P1, int I 
> 
struct Test< T<P1, arg<I> > >{ 
    typedef typename Traits<I>::inner inner;  
}; 

template< 
    template<class, class> class T, 
    class P2, int I 
> 
struct Test< T<arg<I>, P2 > >{ 
    typedef typename Traits<I>::inner inner;  
}; 

// and a bunch of other partial specializations 

int main(){ 

    typename Test<Foo<int, arg<1> > >::inner::impl<int> b; 
    typename Test<Foo<int, arg<2> > >::inner::impl<int, double> c; 
} 

Explicación: Básicamente se trata de una extensión de la idea de la especialización parcial, sin embargo, la diferencia es que en lugar de especializarse en Test, delegue en una clase específica que puede ser especializada en I solo.De esta forma solo necesita definir versiones de inner para cada Iuna vez. Luego múltiples especializaciones de Test pueden reutilizarse. El soporte inner se utiliza para hacer que el typedef en la clase Test sea más fácil de manejar.

EDIT: aquí es un caso de prueba que muestra lo que sucede si se pasa en el número incorrecto de argumentos de plantilla: http://ideone.com/QzgNP

+0

He publicado el mismo código simplificado, pero el que pregunta está diciendo que la plantilla no será tan simple como 'plantilla '. – iammilind

+0

@iammilind, dije que es una extensión, el código es ligeramente diferente, en lo anterior, la especialización de 'I' se delega en la clase' Traits', y la clase 'Test' usa eso, en tu respuesta, estabas hablando de especializar 'Test' (o' X') para cada 'I' - que el OP no está dispuesto a hacer. – Nim

+0

estás en lo correcto. Lo siento, me lo perdí. Gracias por poining. – iammilind

0

Se puede utilizar una función de meta (aquí: inline boost::mpl::if_c, pero podría ser arbitrariamente compleja) a selecciona el que quieras. Es necesario un andamio para poder utilizar constructores, sin embargo:

template <int I> 
class X { 
    template <typename T1> 
    class YforIeq1 { /* meat of the class */ }; 
    template <typename T1, typename T2> 
    class YforIeq2 { /* meat of the class */ }; 
public: 
    template <typename T1, typename T2=boost::none_t/*e.g.*/> 
    struct Y : boost::mpl::if_c<I==1,YforIeq1<T1>,YforIeq2<T1,T2> >::type { 
     typedef typename mpl::if_c<I==1,YforIeq1<T1>,YforIeq2<T1,T2> >::type YBase; 
     /* ctor forwarding: C++0x */ 
     using YBase::YBase; 
     /* ctor forwarding: C++03 (runs into perfect fwd'ing problem)*/ 
     Y() : YBase() {} 
     template <typename A1> 
     Y(const A1&a1) : YBase(a1) {} 
     template <typename A1, typename A2> 
     Y(const A1&a1, const A2&a2) : YBase(a1,a2) {} 
     // ... 
    }; 
}; 

Si hay un problema con ambos YforIeqN se crea una instancia para cada X, entonces puede intentar envolviéndolos como una función de meta nularia (algo a lo largo la forma en que lo hace mpl::apply) y usa mpl::eval_if_c.

Cuestiones relacionadas