我是否总是必须在模板参数的末尾放置可变参数模板参数?
template <size_t begin = 0U, typename... Tp>
void foo(tuple<Tp...> t);
例如,我得到了各种错误:
#include <functional>
#include <iostream>
#include <string>
#include <tuple>
using namespace std;
template <typename... Tp, size_t begin = 0U>
enable_if_t<begin == sizeof...(Tp), void> foo(tuple<Tp...>& t){
cout << endl;
}
template <typename... Tp, size_t begin = 0U>
enable_if_t<begin < sizeof...(Tp), void> foo(tuple<Tp...>& t) {
cout << get<begin>(t) << ' ';
foo<Tp..., begin + 1>(t);
}
int main() {
tuple<int, string, float> t = make_tuple(42, "Jonathan Mee", 13.13);
foo(t);
}
当run on gcc 5.1给我时:
prog.cpp:实例化
std::enable_if_t<(begin < sizeof... (Tp)), void> foo(std::tuple<_Elements ...>&) [with Tp = {int, std::basic_string<char, std::char_traits<char>, std::allocator<char> >, float}; unsigned int begin = 0u; std::enable_if_t<(begin < sizeof... (Tp)), void> = void]
:
prog.cpp:21:7:从这里要求
prog.cpp:15:23:错误:没有匹配函数来调用foo(std::tuple<int, std::basic_string<char, std::char_traits<char>, std::allocator<char> >, float>&)
foo<Tp..., begin + 1>(t);
prog.cpp:8:43:注意:候选人:template<class ... Tp, unsigned int begin> std::enable_if_t<(begin == sizeof... (Tp)), void> foo(std::tuple<_Elements ...>&)
enable_if_t<begin == sizeof...(Tp), void> foo(tuple<Tp...>& t){
prog.cpp:8:43:注意:模板参数扣除/替换失败:
prog.cpp:13:42:注意:候选人:template<class ... Tp, unsigned int begin> std::enable_if_t<(begin < sizeof... (Tp)), void> foo(std::tuple<_Elements ...>&)
enable_if_t<begin < sizeof...(Tp), void> foo(tuple<Tp...>& t) {
prog.cpp:13:42:注意:模板参数扣除/替换失败:
当参数换成:
时template <size_t begin = 0U, typename... Tp>
void foo(tuple<Tp...> t);
程序运行正常:http://ideone.com/SozUbb
如果真的要求可变参数模板参数是最后的,有人可以给我一个关于这些信息的来源吗?
答案 0 :(得分:7)
问题不在于模板声明。这非常好:
template <typename... Tp, size_t begin = 0U>
void foo(tuple<Tp...> t);
问题是这个电话:
foo<Tp..., begin + 1>(t);
虽然可以在参数包之后提供默认模板参数,但您无法在以后实际设置它。编译器无法知道包的结束位置以及包开始后的参数。
你应该翻转排序,将begin
作为第一个参数,默认为:
template <size_t begin = 0U, typename... Tp>
void foo(tuple<Tp...> t);
这样您的递归调用可以是:
foo<begin + 1>(t);
答案 1 :(得分:3)
你错了 - 变异论证并不是最后的 - 但它对你没有帮助。
当您尝试将begin
设置为与0
不同时,您的错误就在递归调用中。在那一行中,编译器无法确定您的begin
应该是std::size_t
参数,并且罢工。
即使在gcc 5.1中编译也很好:
template <class... Tp, std::size_t begin = 0U>
auto foo(std::tuple<Tp...>& t) -> std::enable_if_t<begin == sizeof...(Tp), void> {
std::cout << '\n';
}
template <class... Tp, std::size_t begin = 0U>
auto foo(std::tuple<Tp...>& t) -> std::enable_if_t<begin < sizeof...(Tp), void> {
std::cout << '\n';
}
(我重写了它以弄清楚它出错的地方,所以它在不重要的方面略有不同)。
它与重要的不同之处在于缺乏递归调用。
顺便说一句,您的打印代码有点尴尬。考虑使用类似for_each_arg
:
template<class F, class...Args>
void for_each_arg(F&& f, Args&&...args) {
using discard=int[];
(void)discard{((
f(std::forward<Args>(args))
),void(),0)...,0};
}
将上述内容与std::apply
混合或自行编写:
namespace details {
template<class F, class Tuple, std::size_t...Is>
decltype(auto) apply( std::index_sequence<Is...>, F&& f, Tuple&& args )
{
return std::forward<F>(f)( std::get<Is>(std::forward<Tuple>(args))... );
}
}
template<class F, class Tuple>
decltype(auto) apply(F&& f, Tuple&& tuple) {
using dTuple = std::decay_t<Tuple>;
return details::apply(
std::make_index_sequence<std::tuple_size<dTuple>::value>{},
std::forward<F>(f),
std::forward<Tuple>(tuple)
);
}
template<class F, class Tuple>
decltype(auto) for_each_tuple_element( F&& f, Tuple&& tuple ) {
return apply(
[&](auto&&...args){
for_each_arg( std::forward<F>(f), decltype(args)(args)... );
},
std::forward<Tuple>(tuple)
);
}
现在你的递归深度不等于元组中元素的数量。
template <class Tuple>
void foo(Tuple&& tuple) {
for_each_tuple_element(
[](auto&& arg){ std::cout << decltype(arg)(arg); },
std::forward<Tuple>(tuple)
);
std::cout << '\n';
}
答案 2 :(得分:1)
根据标准§14.1/ 11模板参数[temp.param]:
如果主类模板或别名模板的模板参数是模板参数包,则它应该是最后一个模板参数。除非可以从参数类型列表中推导出该模板参数,否则函数模板的模板参数包不应该跟随另一个模板参数 函数模板或具有默认参数。
因此,您的设置是正确的,因为可变参数后跟默认模板参数。但是,您有语法错误,您应该更改为:
template <typename... Tp, size_t begin = 0U>
^^^^^^
void foo(tuple<Tp...> t);
也就是说,在模板参数列表中...
必须在Tp
之前。