以下代码
#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}); }
正在正确编译(在Debian/Sid/x86-64上使用GCC 6.3),我希望它能像
auto vec = make_from_ints(1,2,3);
返回指向某些包含1、2、3的整数的 vector 的指针
但是,如果我将
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的 double vector 的指针?
顺便说一句,为C++ 14编译(使用
g++ -Wall -std=c++14 -c basiletemplates.cc
),并使用clang++
(版本3.8.1)而不是g++
不会更改任何内容。 最佳答案
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
为其类型(或对SFINAE-使用std::enable_if
友善)。这是一个例子: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 << " ";
live example on wandbox
请注意,我在使用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>{}...));