带双打的C ++可变参数模板

时间:2017-04-03 09:01:19

标签: c++ c++11 variadic-templates

以下代码

#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 ...>
                 ^~~

我不明白为什么。毕竟intdouble都是标量数字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++不要改变任何内容。

1 个答案:

答案 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

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>{}...));