以下代码
#include <initializer_list>
#include <vector>
template<int ...>
const std::vector<int>*make_from_ints(int args...)
{ return new std::vector<int>(std::initializer_list<int>{args}); }
正在正确地编译(使用GCC 6.3,在Debian / Sid / x86-64上),我希望它能用于像
这样的调用auto vec = make_from_ints(1,2,3);
返回指向包含1,2,3的整数向量的指针。
但是,如果我将int
替换为double
,那就是我添加以下内容(在相同的basiletemplates.cc
文件...中)代码:
template<double ...>
const std::vector<double>*make_from_doubles(double args...)
{ return new std::vector<double>(std::initializer_list<double>{args}); }
我收到编译错误:
basiletemplates.cc:8:17: error: ‘double’ is not a valid type
for a template non-type parameter
template<double ...>
^~~
我不明白为什么。毕竟int
和double
都是标量数字POD类型(在C ++ 11标准中预定义)。
如何获取模板可变参数函数以便能够编码:
auto dvec = make_from_doubles(-1.0, 2.0, 4.0);
并获得指向包含-1.0,2.0,4.0的双精度矢量的指针?
BTW,编译C ++ 14(使用g++ -Wall -std=c++14 -c basiletemplates.cc
),使用clang++
(版本3.8.1)代替g++
不要改变任何内容。
答案 0 :(得分:23)
template<int ...>
const std::vector<int>*make_from_ints(int args...)
{ return new std::vector<int>(std::initializer_list<int>{args}); }
上面的代码段有很多问题:
返回const std::vector<int>*
而不是std::vector<int>
,并且不必要地使用动态分配。
std::make_unique
代替new
。您已将make_from_ints
定义为模板函数,该模板函数可获取任意数量的int
模板参数,但您并未提供int
个名称 - 您永远不会使用它们!
您的签名实际上被解析为make_from_ints(int args, ...)
- 这是一个与可变参数模板无关的C va_args
签名。
type... name
。如果要接受与模板参数推导配合使用的特定类型的任意数量的参数,最简单的方法是使用接受的常规可变参数模板任意数量的类型和static_assert
类型(或使用std::enable_if
表示SFINAE友好性)。这是一个例子:
template <typename... Ts>
auto make_from_ints(Ts... xs)
{
static_assert((std::is_same<Ts, int>::value && ...));
return std::vector<int>{xs...};
}
template <typename... Ts>
auto make_from_doubles(Ts... xs)
{
static_assert((std::is_same<Ts, double>::value && ...));
return std::vector<double>{xs...};
}
用法:
for(auto x : make_from_ints(1,2,3,4)) std::cout << x << " ";
std::cout << "\n";
for(auto x : make_from_doubles(1.0,1.5,2.0,2.5)) std::cout << x << " ";
1 2 3 4
1 1.5 2 2.5
请注意,我使用C++17 fold expression检查所有Ts...
是否属于特定类型:
static_assert((std::is_same<Ts, int>::value && ...));
如果您无法访问C ++ 17功能,可以使用以下内容轻松替换:
template <typename... Ts>
constexpr auto all_true(Ts... xs)
{
for(auto x : std::initializer_list<bool>{xs...})
if(!x) return false;
return true;
}
// ...
static_assert(all_true(std::is_same<Ts, int>{}...));