1

Im folgenden Beispiel versuche ich, ein Template-Parameterpaket im Grunde Alias ​​zu verwenden.Typ Alias ​​ein Template-Parameterpaket

Im Standard ist es nicht möglich, also habe ich Leute gefunden, die mit einem Tuple oder einer leeren Templatestruktur umgehen. Allerdings scheint meine Situation anders, weil Ich habe kein Argument Parametertyp Pack müssen passen ...

(Ich weiß, diese Probe dumm scheint, aber es ist ein minimaler PoC. In der Codebasis der Typliste viel länger wird und das wäre wirklich nützlich sein)

#include <iostream> 
#include <tuple> 

template<typename T> 
void f(T t) 
{ 
    std::cout << "templated f()" << std::endl; 

} 
template<> 
void f(int t) 
{ 
    std::cout << "specialized f()" << std::endl; 
} 

template<typename A, typename B> 
void fn() 
{ 
    f(A()); 
} 

template<int, char> 
void fn() 
{ 
    int a; 
    f(a); 
} 

// Can't seem to define this correctly. 
//using MyType = std::tuple<int, char>()::type; 

int 
main(int, char**) 
{ 
    fn<char, char>(); 

    // I have 
    fn<int, char>(); 

    // I want some alias; 
    //fn<MyType>(); 

    return 0; 
} 

Referenzen.

Antwort

2

Wenn wir ein Templat leer Struktur verwenden, um die Art Pack alias ist es möglich, SFINAE auf Ihrer ursprüngliche Funktion zu verwenden, so dass, wenn es für unsere spezielle Struktur es einfach nach vorne aufgerufen wird das gekapselte Typenpaket für die ursprüngliche Implementierung.

Hier ist, wie das in aussehen würde C++ 14:

template <typename... Types> 
struct TypePack {}; 

// Helper meta functions to determine 
// if the template param is a TypePack struct. 
template <typename... Ts> 
static constexpr bool is_typepack_v = false; 

template <typename... Ts> 
static constexpr bool is_typepack_v<TypePack<Ts...>> = true; 

template <typename... Ts> 
// Enabled whenever template parameter is not a TypePack. 
typename std::enable_if<not is_typepack_v<Ts...>>::type 
fn() { // FN_A 
    std::cout << "Generic\n"; 
} 

template <> 
// Complete specialization, selected over generic fn for <char,int> 
void fn<char,int>() { // FN_B 
    std::cout << "Specialized for char,int\n"; 
} 

template <typename T> 
// Enabled when template param is a TypePack. 
typename std::enable_if<is_typepack_v<T>>::type 
fn() { // FN_C 
    forward_fn(T{}); 
} 

// forward_fn() extracts the types from a TypePack argument 
// and invokes fn with it. 
template <typename ...T> 
void forward_fn(TypePack<T...> /*unused*/) { 
    std::cout << "Forwarding a pack alias\n"; 
    fn<T...>(); 
} 

// To alias a type pack use TypePack<T1,T2..,Tn> 
using CharIntPack = TypePack<char,int>; 

int main() { 
    fn<char,char>(); // invokes FN_A 
    fn<char,int>(); // invokes FN_B 
    fn<CharIntPack>(); // invokes FN_C which then invokes FN_B 
    return 0; 
} 

Dies erzeugt die folgende Ausgabe:

Generic 
Specialized for char,int 
Forwarding a pack alias 
Specialized for char,int 

Was ich über diese Methode mag, ist, dass dieser "Trick "wird nur einmal bei der Definition der Funktionen benötigt, und der Benutzer kann sich darüber völlig im Klaren sein.

1

Ein Parameter Pack hat genau das, eine Liste von mehreren Typen. Es ist möglich, ein Parameterpaket (sobald Sie eins haben) mit dem ... -Operator weiterzuleiten und es wieder in eine Liste zu erweitern, aber Sie müssen diese Typen haben, um damit zu beginnen.

Und beachten Sie, dass: mit MyType = std :: tuple() :: type; wird nicht funktionieren, weil Tuple kein 'Typ' Mitglied hat. Sie können nicht einmal tun (mit oder ohne die ...): Vorlage Strukturhalter { typedef Typen ... Typ; }; mit my_type = holder :: type;

1

In dem folgenden Beispiel versuche ich, ein Vorlage-Parameterpaket im Grunde Alias ​​eine Vorlage .

Es ist nicht möglich in der Norm, so dass ich Leute um die Einschränkung mit einem Tuple oder leere Templates Struktur arbeiten.

std::tuple<...> ist für Parameter für Aliasing tun Packs!

Allerdings scheint meine Situation anders, weil ich kein Argument haben Parameter Typ Pack zu entsprechen ...

Nun, diese ein Fälle, in denen Sie herausfinden, dass Klasse-Vorlagen sind tatsächlich mächtiger als funktions Vorlagen - in diesem Fall, dass wir Teil Template-Spezialisierungen haben; Also, wenn Sie sich entscheiden, mit einem überladenen operator() eine Klasse-Vorlage zu verwenden, habe ich sagen, ist es möglich:

Demo:

template<typename... A> 
struct fn 
{ 
    void operator()() const 
    { std::cout << "primary f()" << std::endl; } 
}; 

template<typename... A> 
struct fn<std::tuple<A...>> 
{ 
    void operator()() const 
    { std::cout << "specialized on unlimited params()" << std::endl; } 
}; 

template<> 
struct fn<std::tuple<char, double*>> 
{ 
    void operator()() const 
    { std::cout << "specialized on f<char, double*>()" << std::endl; } 
}; 

template<typename A, typename B> 
struct fn<std::tuple<A, B>> 
{ 
    void operator()() const 
    { std::cout << "specialized on two params f()" << std::endl; } 
}; 

// Can't seem to define this correctly. 
using MyType = std::tuple<int, char>; 
using MyType2 = std::tuple<char, double*>; 
using MyType3 = std::tuple<int, char, double, void*>; 

int main() 
{ 
    fn<int, char>()(); 
    fn<MyType>()(); 
    fn<MyType2>()(); 
    fn<MyType3>()(); 
    return 0; 
} 

Ausgang:

primary f() 
specialized on two params f() 
specialized on f<char, double*>() 
specialized on unlimited params() 
+0

Danke, das ist eine nette Lösung Ich mag einfach nicht die Schnittstelle, die die Funktoren benötigen, wenn sie nicht freistehende Funktionen, sondern Klassenmitglieder sind. Sie hätten 'object :: fn()' anstelle von 'object.fn()'. – dzan

0

Da C 14 ++, wenn Sie wollen nicht Ihre Funktion überlasten oder umschreiben, noch Sie wollen eine Funktors Klassenvorlage schreiben, können Sie ein allgemeines Lambda verwenden, um lokal eine Typliste in ein Parameter-Pack drehen:

template<typename T> 
struct quote{ using type = T; }; 

template<typename T> 
using unpack = typename T::type; 

template<typename... T> 
using params = std::tuple<quote<T>...>; 

// to be used as 

template<typename A, typename B> 
void foo(); 

using my_params = params<int,char>; 

int main() 
{ 
    std::apply([](auto... params){ foo< unpack<decltype(params)>... >(); }, my_params{}); 
} 

PS:std::apply erfordert C++ 17, kann aber in> = C++ 11 als auch ...

PPS realisiert werden: in C++ 20, wir können sogar []<typename... T>(){ foo<T...>(); } schreiben so dass die Lösung noch sauber ...

Verwandte Themen