enable_if + constructor
-
problem: eine template klasse erbt von einem iher template parameter, dessen konstruktor u.U. selbst parameter zur initialisierung braucht. gibt es eine möglichkeit, den richtigen konstruktor in der template klasse 'freizuschalten' ? das hier geht leider nicht :
template< typename T > struct is_void { static const bool value = false; }; template<> struct is_void< void > { static const bool value = true; }; struct A { A(int) { } typedef int arg_type; }; struct B { B() { } typedef void arg_type; }; template< typename T > struct C : public T { C(typename boost::enable_if< is_void< typename T::arg_type > >::type* = 0) : T() { } C(typename T::argtype arg, typename boost::disable_if< is_void< typename T::arg_type > >::type* = 0) : T( arg ) { } }; int main() { C<A> a(1); C<B> b; }
-
Das Problem ist ja, dass es enable_if<T>::type bzw. disable_if<T>::type nicht
gibt, sollte T == false sein.D. h., einer deiner Bedingungen fuehrt unweigerlich zu einem Compiler-Fehler.
Wie man das umgehen kann, kann ich dir so aus dem Bauch heraus leider nicht
sagen, sry. Aber ich lass mir das noch was durch den Kopf gehen.mfg
v R
-
Wie wäre es, wenn du is_void übergibst?
template< typename T,bool b> struct C : public T { //mit nichtvoid-konstruktor }; template<class T> struct C<T,true>:public T { //voidkonstruktor };
leider ist das nicht generisch... hm
template<class T> struct C:public T { template<class U=int> C(typename T::arg_type arg,U()) { STATIC_CHECK(!is_void<typename T::arg_type>::value); //... }; template<class U=int> C(U()) { STATIC_CHECK(is_void<typename T::arg_type>::value); //... }; };
Ungetestet!!!
/edit: dann muss man immer 0 übergeben, aber mir fällt nix anderes ein...
-
Hallo,
ich weiß nicht, ob ich das Problem richtig verstanden habe, aber hilft dir vielleicht Loki::Int2Type?
-
virtuell Realisticer schrieb:
Das Problem ist ja, dass es enable_if<T>::type bzw. disable_if<T>::type nicht
gibt, sollte T == false sein.D. h., einer deiner Bedingungen fuehrt unweigerlich zu einem Compiler-Fehler.
Wie man das umgehen kann, kann ich dir so aus dem Bauch heraus leider nicht
sagen, sry. Aber ich lass mir das noch was durch den Kopf gehen.mfg
v Rdas ist ja eigentlich auch der gedanke dabei, das zusammen mit SFINAE genau die konstruktoren übrigbleiben, die sinn machen. das klappt hier aber offenbar nicht, obwohl T::arg_type ein abhängiger typ ist. ness' idee ist auch nett, klaptt leider nicht weil funktionstemplates keine default parameter haben können und void so auch nicht als funktionsparameter verwendet werden kann. ausserdem ist ein template konstruktor nie ein default konstruktor und damit fällt die möglichkeit ein funktionstemplate zu benutzen im prinzip sowieso ins wasser. die nicht generische variante von ness is auch unschön, denn C ist ja gerade das, was als gemeinsamkeit aller templates übrig bleibt nachdem unterschiedliches verhalten in den policy parameter verbannt wurde. ich schätze der einfachste workaround hier ist hier tatsächlich, dass alle policies grundsätzlich irgendeinen parameter erfordern und diesen dann gegebenenfalls verwerfen. aber vielleicht weiss einer von der gurus rat?
-
@camper
Du machst hier einen Denkfehler. Bei den beiden Konstruktoren handelt es sich *nicht* um Templates. Demzufolge wird SFINAE hier überhaupt nicht angewendet.Was spricht gegen ein simples:
struct NoArg {}; struct A { A(int) { } typedef int arg_type; }; struct B { B() { } typedef NoArg arg_type; }; template <class T> struct Arg {typedef T type;}; template <> struct Arg<NoArg> { private: struct Dummy {}; public: typedef Dummy type; }; template< typename T > struct C : public T { C() : T() {} C(typename Arg<typename T::arg_type>::type a) : T(a) {} }; int main() { C<A> a(1); C<B> b; }
-
Zusatz:
Falls dich das typedef NoArg arg_type in B stört, kannst du das natürlich komplett weglassen und stattdessen noch ein HasArgType-Template bauen:namespace detail { template < typename Valid > struct Has { typedef char type; }; template <class T> struct HasArgType { private: template <class X> static typename Has<typename X::arg_type>::type check(int); template <class X> static int check(...); public: enum { value = sizeof(check<T>(0)) == 1}; }; template <class T, bool b> struct Arg {typedef typename T::arg_type type;}; template <class T> struct Arg<T, false> { private: struct NoArg {}; public: typedef NoArg type; }; } struct A { A(int) { } typedef int arg_type; }; struct B { B() { } }; template< typename T > struct C : public T { C() : T() {} C(typename detail::Arg<T, detail::HasArgType<T>::value >::type a) : T(a) {} };
Damit machst du wieder von zwei Techniken gebrauch:
1. Funktionen eines Templates werden nur bei Bedarf instanziiert (Ctor-Überladung)
2. SFINAE in HasArgType.
-
so geht es. eigentlich geht es sogar direkt ohne umwege:
struct A { A(int) { } typedef int arg_type; }; struct B { B() { } typedef int arg_type; }; template< typename T > struct C : public T { C() : T() {} C(typename T::arg_type a) : T(a) {} }; //template C<A>; //template C<B>; int main() { C<A> a(1); C<B> b; }
das problem - was ich übersehen hatte in dem code in dem es auftrat - sind die auskommentierten zeilen. die betreffenden klassen sind relativ komplex, so dass sich explizite instanziierung lohnt (besonders wenn sie in vorkompilierte header verbannt werden). das schlägt dann allerdings leider fehl - also muss ich wohl darauf verzichten.