2017-06-12 14 views
5

sagen, dass ich einige Template-Typ haben ...Passing angegebenen Vorlagentyp als Template-Parameter

template <typename T> struct Foo { 
    Foo(T t) {} 
}; 

Gibt es eine Möglichkeit, einen bestimmten Foo Typ eine Funktion übergeben, so dass die Funktion direkt sichtbar T hat?

Idealerweise würde ich in der Lage sein, so etwas zu schreiben ...

Foo<int> foo = create<Foo<int>>(); 

Die nächstgelegene ich habe in der Lage zu kommen, ist

template < 
    template <typename> typename TT, 
    typename T, 
    std::enable_if_t<std::is_same<TT<T>, Foo<T>>::value, int> = 0 
> 
Foo<T> create() { 
    return Foo<T>(T()); 
} 

, die dann wie

verwendet werden würden
Foo<int> foo = create<Foo, int>(); 

Danke für jede Hilfe.

+0

Also, im Wesentlichen, wollen Sie eine 'create' Funktion, die als Vorlage Typ nur ein' Foo 'nimmt und ein' Foo 'zurückgibt? – NathanOliver

+2

Könnte es nicht einfach sein 'Foo foo = createFoo ();' - als der konstruierte Typ wird immer 'Foo ' sowieso? – axalis

+0

@axalis ja, ich könnte 'createFoo ()' anstelle von 'create >()', aber ich bin auf der Suche nach einer Lösung, die ich für andere Typen, z. 'create >' – Daskie

Antwort

5

Diese Form der Template-Template-Parameter wird nur in C++ 17 erlaubt:

template < //   v---------- class allowed 
    template <typename> class TT, 
    typename T, 
    std::enable_if_t<std::is_same<TT<T>, Foo<T>>::value, int> = 0 
> 
Foo<T> create() { 
    return Foo<T>(T()); 
} 

In C++ 17, beide:

template < //   v---------- typename here not allowed 
    template <typename> typename TT, 
    typename T, 
    std::enable_if_t<std::is_same<TT<T>, Foo<T>>::value, int> = 0 
> 
Foo<T> create() { 
    return Foo<T>(T()); 
} 

Sie die typename von class wies darauf hin, ersetzen müssen kompiliert und sind gleichwertig.


Um Ihre Syntax Foo<int> foo = create<Foo<int>>(); Arbeit zu machen, müssen Sie einfach, dies zu tun:

// default case has no typedef 
template<typename> 
struct first_param {}; 

// when a template is sent, define the typedef `type` to be equal to T 
template<template<typename> class TT, typename T> 
struct first_param<TT<T>> { 
    using type = T; 
}; 

// template alias to omit `typename` everywhere we want to use the trait. 
template<typename T> 
using first_param_t = typename first_param<T>::type; 
:

template <typename T> 
T create() { 
    return T{}; 
} 

Wenn Sie einschränken möchten, welche Art gesendet werden, können Sie einen Typ Merkmal erstellen müssen

Dann Ihr Merkmal verwenden:

template < 
    typename T, 
    void_t<first_param_t<T>>* = nullptr 
> //  ^---- if the typedef is not defined, it's a subtitution error. 
T create() { 
    return T(first_param_t<T>{}); 
} 

Sie können void_t wie folgt implementieren:

template<typename...> 
using void_t = void; 

Live at Coliru

+0

Ich glaube nicht, dass dies das Problem ist, wie es scheint, für das OP zu kompilieren. Die Frage scheint zu sein, wie man die Schablone "repariert", um einem zu erlauben, zu schreiben 'Foo foo = create >();' anstelle von 'Foo foo = create ();' – NathanOliver

+0

@NathanOliver örtlich festgelegt, danke für das Zeigen aus. –

+1

Ehrfürchtig. Sollte jetzt funktionieren, egal was das OP will. +1 – NathanOliver

4

Warum nicht einfach einen Tag Disposition verwenden, zB:

template <class> 
struct tag { }; 

template <class T> 
Foo<T> create(tag<Foo<T>>) { 
    return Foo<T>(T()); 
} 

//... 

Foo<int> foo = create(tag<Foo<int>>{}); 
+0

Das würde absolut funktionieren, aber ich suche eine Lösung ohne Funktionsparameter. – Daskie

+0

@Daskie überdenken Sie Ihre Architektur - der Funktionsaufruf wird von einem Compiler optimiert, als würde er sowieso keinen Parameter haben ... –

2

In C++ 11

Demo

Der Kernpunkt ist eine Einstiegspunktfunktion namens create, die eine create_helper Struktur instanziieren kann, um den richtigen Typ zu erstellen.

Wir können unsere Strukturen mithilfe der Vorlagenspezialisierung erstellen, sodass eine Vorlagenklasse erzwungen wird.

Voll Code:

template<class T> 
struct create_helper 
{ 
    static_assert(sizeof(T) == 0, "Need to pass templated type to create"); 
}; 

template <class T, template<class> class TT> 
struct create_helper<TT<T>> 
{ 
    static TT<T> apply() 
    { 
     return {T{}}; 
    } 
}; 

template<class T> 
auto create() -> decltype(create_helper<T>::apply()) 
{ 
    return create_helper<T>::apply(); 
} 

Und ein Test:

template<class T> 
struct Foo 
{ 
    Foo(T t){std::cout << "Constructed Foo with value " << t << std::endl;} 
}; 
int main() 
{ 
    Foo<int> foo = create<Foo<int>>(); 
} 

Ausgang:

Constructed Foo with value 0 
+0

Beachten Sie, dass die Basisvorlage 'create_helper' nicht mit einem' static_assert' enden muss. Wenn Sie möchten, können Sie eine 'apply'-Funktion hinzufügen, um eine Standardinstanz von' T' zu erstellen, wie wir es bei der Spezialisierung getan haben. [Demo] (https://wandbox.org/permlink/bAcedPFmk3I1svmI) – AndyG

3

Eine einfache Möglichkeit ist es, die Untertyp-Information in Foo direkt hinzuzufügen:

template <typename T> struct Foo { 
    using type = T; 
    Foo(T t) {} 
}; 

und dann

template <typename FooT> 
FooT create() { 
    return FooT(typename FooT::type{}); 
} 

Sie könnten SFINAE hinzufügen, wenn Sie wollen:

template <typename FooT> 
auto create() 
-> decltype(FooT(typename FooT::type{})) 
{ 
    return FooT(typename FooT::type{}); 
} 

Wenn Sie wirklich will, um die Funktion zu Foo ausschließlich beschränken, Sie haben einen Zug und SFINAE auf sich zu schaffen.

+0

Traits und SFINAE scheint der Weg zu gehen – Daskie