当variadic模板不是最后一个参数时,如何重载它们

Jos*_*G79 9 c++ variadic-templates c++11

基本上问题可以用这个例子来概括:

template <typename ...Us>
void foo(Us...) { std::cout << "A\n"; }

template <typename ...Us>
void foo(Us..., int) { std::cout << "B\n"; }

int main(){
  foo(1,2,3);
}
Run Code Online (Sandbox Code Playgroud)

这称为第一个foo(打印A).我怎么能让它拨打第二个foo

如果这使用了非变量模板,或者"int"是第一个参数,那么重载规则将调用正确的函数.也就是说,特定类型(int)是比模板更好的匹配,因此它将调用第二个foo.但显然,变量模板的情况并非如此?当变量模板不是最后一个参数时,有没有办法重载变量模板?

0x4*_*2D2 6

当参数包没有出现在参数声明的最后时,它是一个非推导的上下文.非推断的上下文意味着必须明确给出模板参数.这就是为什么foo#1更好的过载.您可以通过提供显式参数(foo<int,int>(1,2,3))来强制执行第二次重载调用,或者如您所说,将其int移到前面.

为了清楚起见,可以使用可变参数模板重载函数,但是当它们不作为最后一个参数出现时,它们不能被推导出来,当没有提供显式参数时,它们会自动取消它们作为候选者的资格.提供它们时,模板参数将替换为其提供的类型,并且生成的非模板函数是重载决策的候选者.

要回答你的问题,你可以把所有参数都放到一个元组中,挑出最后一个并测试那个.然后基于一个简单的is_same检查传递一个重载:

template<class...Us>
void foo_impl(true_type,Us...); // last argument is int
template<class...Us>
void foo_impl(false_type,Us...); // last argument non-int

template<class...Us>
void foo( Us&&...us ) {
  using tuple=tuple<Us...>;
  using last=decltype(get<sizeof...(Us)-1>(declval<tuple>()));
  foo_impl(is_same<decay_t<last>,int>{}, forward<Us>(us)...);
}
Run Code Online (Sandbox Code Playgroud)


W.F*_*.F. 5

您可以使用SFINAE提供一些std::tuple功能帮助(支持c ++ 11的代码):

#include <type_traits>
#include <tuple>
#include <iostream>

template <typename ...Us>
typename std::enable_if<!std::is_same<typename std::tuple_element<sizeof...(Us) - 1, std::tuple<Us...>>::type, int>::value>::type foo(Us...) { 
   std::cout << "A\n"; 
}

template <typename ...Us>
typename std::enable_if<std::is_same<typename std::tuple_element<sizeof...(Us) - 1, std::tuple<Us...>>::type, int>::value>::type foo(Us...) { 
    std::cout << "B\n"; 
}

int main(){
  foo(1,2,3);
}
Run Code Online (Sandbox Code Playgroud)

输出:

如果您希望它测试pack中的某个其他参数是否属于给定类型,则只需将std::tuple_element第一个参数更改为所需的索引值.

[现场演示]


如果你也希望利用参数包的其他参数,例如通过递归调用,那么你的麻烦就更大...... c ++ 11没有创建索引包的功能.您需要自己实现该功能

#include <tuple>
#include <utility>
#include <iostream>
#include <initializer_list>

template <class T, T... Vs>
struct integer_sequence { };

template <class T, class, class, class = integer_sequence<T>, class = integer_sequence<T, 0>, class = void>
struct make_integer_sequence_impl;

template <class T, T ICV1, T... Res, T... Pow>
struct make_integer_sequence_impl<T, std::integral_constant<T, ICV1>, std::integral_constant<T, 0>, integer_sequence<T, Res...>, integer_sequence<T, Pow...>, typename std::enable_if<(ICV1 > 0)>::type>: make_integer_sequence_impl<T, std::integral_constant<T, ICV1/2>, std::integral_constant<T, ICV1%2>, integer_sequence<T, Res...>, integer_sequence<T, Pow..., (Pow + sizeof...(Pow))...>> { };

template <class T, T ICV1, T... Res, T... Pow>
struct make_integer_sequence_impl<T, std::integral_constant<T, ICV1>, std::integral_constant<T, 1>, integer_sequence<T, Res...>, integer_sequence<T, Pow...>, void>: make_integer_sequence_impl<T, std::integral_constant<T, ICV1/2>, std::integral_constant<T, ICV1%2>, integer_sequence<T, Pow..., (Res + sizeof...(Pow))...>, integer_sequence<T, Pow..., (Pow + sizeof...(Pow))...>> { };

template <class T, class Res, class Pow>
struct make_integer_sequence_impl<T, std::integral_constant<T, 0>, std::integral_constant<T, 0>, Res, Pow, void> {
   using type = Res;
};

template <class T, T V>
using make_integer_sequence = typename make_integer_sequence_impl<T, std::integral_constant<T, V/2>, std::integral_constant<T, V%2>>::type;

template <size_t V>
using make_index_sequence = make_integer_sequence<size_t, V>;

template <size_t... V>
using index_sequence = integer_sequence<size_t, V...>;

void foo() { }

template <typename ...Us>
void foo(Us... us);

template <typename ...Us, std::size_t... Is>
typename std::enable_if<!std::is_same<typename std::tuple_element<sizeof...(Us) - 1, std::tuple<Us...>>::type, int>::value>::type foo_impl(index_sequence<Is...>, Us... us) { 
    std::cout << std::get<sizeof...(Us) - 1>(std::forward_as_tuple(us...)) << "A\n";  
   foo(std::get<Is>(std::forward_as_tuple(us...))...);
}

template <typename ...Us, std::size_t... Is>
typename std::enable_if<std::is_same<typename std::tuple_element<sizeof...(Us) - 1, std::tuple<Us...>>::type, int>::value>::type foo_impl(index_sequence<Is...>, Us... us) { 
    std::cout << std::get<sizeof...(Us) - 1>(std::forward_as_tuple(us...)) << "B\n"; 
   foo(std::get<Is>(std::forward_as_tuple(us...))...);
}

template <typename ...Us>
void foo(Us... us) {
    foo_impl(make_index_sequence<sizeof...(Us) - 1>{}, std::forward<Us>(us)...);
}

int main(){
  foo(1,2,3);
}
Run Code Online (Sandbox Code Playgroud)

[现场演示]

或重新考虑参数访问模式:

#include <iostream>

void foo() { }

template <typename Other, typename ...Us>
void foo(Other first, Us... rest) { 
    foo(rest...);
    std::cout << first << "A\n"; 
}

template <typename ...Us>
void foo(int first, Us... rest) { 
    foo(rest...);
    std::cout << first << "B\n"; 
}

int main(){
  foo(1,2,3);
}
Run Code Online (Sandbox Code Playgroud)

[现场演示]

  • 请注意,就编译时内存和时间而言,`tuple`是一种非常重要的方法.但它已经写好了,所以...... (2认同)