输入别名模板参数包

问题描述 投票:1回答:4

在下面的示例中,我试图基本上为模板参数包添加别名。

这在标准中是不可能的,所以我发现人们使用元组或空模板结构来解决限制。但是我的情况似乎有所不同,因为我没有参数类型包匹配...

(我知道这个示例似乎很愚蠢,但它是一个最小的PoC。在代码库中,类型列表会变得更长,这真的很有用。)

#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;
}

引用;

c++ templates variadic-templates template-meta-programming
4个回答
2
投票

如果我们使用模板化的空结构来对类型包进行别名,则可以在原始函数上使用SFINAE,这样当为我们的特殊结构调用它时,它只是将封装的类型包转发到原始实现。

以下是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;
}

这会产生以下输出:

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

我喜欢这种方法的方法是,在定义函数时只需要一次“技巧”,用户可以完全不知道它。


1
投票

参数包必须是几个类型的列表。可以通过在其上使用...运算符转发参数包(一旦有一个参数包),从而将其重新扩展回列表,但您必须首先使用这些类型。

请注意:使用MyType = std :: tuple():: type;因为元组没有“类型”成员,所以不会起作用。你甚至不能做(有或没有......):template struct holder {typedef Types ... type; };使用my_type = holder :: type;


1
投票

在下面的示例中,我试图基本上为模板参数包添加别名。

这在标准中是不可能的,所以我发现人们使用元组或空模板结构来解决限制。

std::tuple<...>非常适合做别名参数包!

但是我的情况似乎有所不同,因为我没有参数类型包匹配...

那么,在这种情况下,您会发现类模板实际上比函数模板更强大 - 在这种情况下,我们可以拥有部分模板特化;因此,如果您决定使用带有重载的operator ()的类模板,我确实说,它可能:

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;
}

输出:

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

0
投票

从C ++ 14开始,如果您不想重载或重写函数,也不想编写仿函数类模板,则可以使用通用lambda在本地将类型列表转换为参数包:

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需要C ++ 17,但也可以在> = C ++ 11中实现......

PPS:在C ++ 20中,我们甚至可以编写[]<typename... T>(){ foo<T...>(); },使解决方案更清晰......

© www.soinside.com 2019 - 2024. All rights reserved.