c++ - 可变参数模板重载解析

标签 c++ c++11 variadic-templates

假设我有一个可以递增的模板参数列表。我想增加这个列表的头部。这是代码

template<int N>
struct Counter {
    static constexpr Counter<N+1> increment();
};
template<int N>
constexpr Counter<N+1> Counter<N>::increment() {
    return Counter<N+1>();
}

//List (will be used as List of Counters)
template <typename... TAIL>
struct List {};

template <typename HEAD, typename... TAIL>
struct List<HEAD,TAIL...> {};



template <typename HEAD, typename... TAIL>
auto incrFront() -> decltype(List<decltype(HEAD::increment()),TAIL...>()) {
    return List<decltype(HEAD::increment()),TAIL...>();
}

它确实有效:

auto l0 = incrFront<Counter<0>>(); // Ok, decltype(l0) == List<Counter<1>>
l0 =  List<Counter<1>>(); //Ok, right type
auto l1 = incrFront<Counter<0>,Counter<1>>();  // Ok, decltype(l1) == List<Counter<1>,Counter<1>>
l1 = List<Counter<1>,Counter<1>>();

现在,我想增加列表的后面,所以

template <typename... HEAD, typename TAIL>
auto incrBack() -> decltype(List<decltype(HEAD...,TAIL::increment())>()) {
    return List<decltype(HEAD...,TAIL::increment()>();
}

但是得到错误 'incrBack' was not declared in this scope

我试图在此方法之前添加另一种方法:

template <typename... HEAD>
auto incrBack() -> decltype(List<HEAD...>()) {
    std::cout << "Should not be here\n";
    return List<HEAD...>();
}

希望这个方法在解析重载时永远不会被调用,但是这个方法确实被调用了。

有什么线索吗?我只想让这个例子正常工作:

auto l2 = incrBack<Counter<1>,Counter<1>>();  // I want decltype(l2) == List<Counter<1>,Counter<2>>
l2 = incrFront<Counter<0>,Counter<2>>();  //should be possible

最佳答案

首先,编写代码以对 List<> 进行操作而不是裸可变类型列表,它会更容易。

其次,不要使用模板函数,而是使用模板类。

template<typename T>
struct inc {
  typedef decltype(T::increment()) type;
};
template<typename T>
using Inc = typename inc<T>::type;

现在我们可以谈谈Inc<HEAD>而不是 decltype( HEAD::increment() ) ,这将使您的代码更具可读性。

写下template类:

template<typename List, typename T>
struct append;
template<typename List, typename T>
using Append = typename append<List,T>::type;
template<template<typename...>class TypeList, typename... Ts, typename T>
struct append<TypeList<Ts...>, T> {
  typedef TypeList<Ts..., T> type;
};
template<typename List>
struct reverse;
template<typename List>
using Reverse = typename reverse<List>::type;
template<template<typename...>class TypeList>
struct reverse<TypeList<>> {
  typedef TypeList<> type;
};
template<template<typename...>class TypeList, typename T0, typename... Ts>
struct reverse<TypeList<T0, Ts...>> {
  typedef Append< Reverse<TypeList<Ts...>>, T0 > type;
};

和类似的。请注意,我对类型进行操作,并且我专注于从传递给 template 的一些通用可变类型包生成的类型。 -- 这意味着我不局限于使用一种方法将可变参数打包成单一类型。

我也写template减少 typename 的别名垃圾邮件。

接下来,应用仿函数:

template<template<typename>class Func, typename List>
struct apply_to_first;
template<template<typename>class Func, typename List>
using ApplyToFirst = typename apply_to_first<Func, List>::type;
template<template<typename>class Func, template<typename...>class TypeList, typename T0, typename... Ts>
struct apply_to_first<Func, TypeList<T0, Ts...>> {
  typedef TypeList< typename Func<T0>::type, Ts... > type;
};

然后 IncFirst :

template<typename List>
using IncFirst = ApplyToFirst< inc, List >;

现在很短。

至于IncLast , 它稍微难一点:

template<typename List>
using IncLast = Reverse< IncFirst < Reverse<List> > >;

但仍然适合一行。不过,我更喜欢这个更详细的版本:

template<template<typename>class Func, typename List>
using ApplyToLast = Reverse< ApplyToFirst< Func, Reverse<List> > >;

template<typename List>
using IncLast = ApplyToLast< inc, List >;

现在,我没有直接回答你的问题,因为我从来没有写过 incrFront (我也没有编译上面的内容,所以它可能充满了语法错误)。

所以这里是 incrFirstincrLast在将繁重的工作移至上述迷你元编程库之后:

template<typename... Ts>
IncFirst<List<Ts...>> incrFirst() {
  return IncFirst<List<Ts...>>();
}
template<typename... Ts>
IncLast<List<Ts...>> incrLast() {
  return IncLast<List<Ts...>>();
}

关于c++ - 可变参数模板重载解析,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16670260/

相关文章:

c++ - 存储指针时 auto 和 auto* 的区别

c++ - 模板类的树,其中 chilldren 是 std::array of std::unique_ptr

c++ - 奇怪的循环模板模式 (CRTP) 和派生的构造函数参数

c++ - 函数重载的顺序是什么

android - 如何使用 getApplicationJniMethodId 发送一个 int 作为参数?

c++ - 共享指针析构函数中的内存顺序

c++ - 为流运算符返回代理类时解包参数

c++ - 将模板可变参数函数及其参数传递给函数

android - dlopen 失败 : cannot locate symbol "cblas_sdsdot" referenced by "libgsl.so"

c++ - 用字符 A-Z 替换字符串中的每个字母(运行时问题)