以下代码
#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 的整数 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
s 一个名字 - 你永远不能使用它们!您的签名实际上被解析为
make_from_ints(int args, ...)
- 这是一个 Cva_args
与可变参数模板无关的签名。- 参数包的正确语法是
type... name
.
- 参数包的正确语法是
如果您想接受与 模板参数推导 配合得很好的特定类型的任意数量的参数,最简单的方法是使用接受的常规 可变参数模板任意数量的类型和static_assert
s 他们的类型(或使用 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>{}...));
关于带 double 的 C++ 可变参数模板,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43180477/