c++ templates conditional-compilation compile-time

c++ - ¿Inclusión/exclusión condicional en tiempo de compilación basada en argumentos de plantilla?



templates conditional-compilation (5)

¿Qué hay de este enfoque - http://sergey-miryanov.blogspot.com/2009/03/template-class-overriding.html ? (lo siento por ruso)

Considere la siguiente clase, con la estructura interna Y utilizada como tipo, por ejemplo. en plantillas, más adelante:

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

Ahora, este ejemplo obviamente no se compilará, con el error de que la segunda 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 su posición puede diferir en diferentes especializaciones parciales (mi estructura real se parece más a esto , la anterior es solo para simplicidad de la pregunta), por lo que me gustaría one class fits every I solución.

Mi primer pensamiento fue obviamente enable_if , pero eso parece fallar, por ejemplo. Todavía tengo 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{}; };

Por lo tanto, ya que enable_if falla, espero que haya otra manera de lograr la siguiente verificación de 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{}; } };

Sería simplemente ahorrarme un montón de duplicación de código, pero estaría realmente feliz si de alguna manera fuera posible.
Edición: Lamentablemente, no puedo usar lo obvio: varias plantillas, ya que estoy usando Visual Studio 2010, así que solo puedo usar las cosas de C ++ 0x que se admiten allí. : /


Aqui tienes:

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, es una extensión de la idea de especialización parcial, sin embargo, la diferencia es que, en lugar de especializarse en Test , delegar en una clase específica que puede especializarse solo en I De esa manera solo necesitas definir versiones de inner para cada I una vez . Luego se pueden reutilizar múltiples especializaciones de Test . El soporte inner se utiliza para facilitar el manejo de typedef en la clase Test .

EDITAR: aquí hay un caso de prueba que muestra lo que sucede si pasa la cantidad incorrecta de argumentos de plantilla: http://ideone.com/QzgNP


Hay dos problemas aquí:

  1. enable_if trabaja con especialización parcial, no con plantillas primarias.
  2. El número de argumentos visibles externamente está determinado por la plantilla principal, de los cuales puede haber solo uno.

Respuesta 1.

Como sugirió en el chat, una lista enlazada de plantillas puede emular el paquete de parámetros 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 terminas con next::next::next Garbage, es fácil escribir una metafunción o usar Boost MPL.

Respuesta 2.

Las plantillas de diferente aridad se pueden nombrar de manera similar pero aún se mantienen distintas si están anidadas dentro del tipo controlado por 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;


Puede usar una función meta (aquí: inlined boost::mpl::if_c , pero podría ser arbitrariamente complejo) para seleccionar la que desea. Sin embargo, necesitas algunos andamios para poder usar constructores:

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 la YforIeq una instancia de YforIeq N para cada X, entonces puede intentar envolverlas como una meta función nula (algo que a lo largo del camino hace mpl::apply ) y usar mpl::eval_if_c .


Puedes probar a continuación (no es 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 que la respuesta sea tan simple !!

Editar (especialización parcial burlona ) : @Xeo, pude compilar el siguiente código y parece estar lleno.

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, puede usar X <1>, X <2> indistintamente. Pero en el ejemplo más amplio que mencionaste, eso es irrelevante. Aún si lo necesita, puede poner cheques para I = 1 y I = 2 .