以下代码

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

我不明白为什么。毕竟intdouble都是标量数字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>{}...));
    

    10-07 19:25
    查看更多