【问题标题】:C++ variadic template with doubles带双精度的 C++ 可变参数模板
【发布时间】:2017-08-28 01:47:21
【问题描述】:

以下代码

#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 的一些双精度向量的指针?

顺便说一句,为 C++14 编译(使用 g++ -Wall -std=c++14 -c basiletemplates.cc),并使用 clang++(版本 3.8.1)而不是 g++ 不会改变任何东西。

【问题讨论】:

  • int args... 被解析为int args, ...,你的模板定义完全错误

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


【解决方案1】:
template<int ...>
const std::vector<int>*make_from_ints(int args...)
{ return new std::vector<int>(std::initializer_list<int>{args}); }

上面的sn-p有很多问题:

  • 返回 const std::vector&lt;int&gt;* 而不是 std::vector&lt;int&gt; 并且不必要地使用动态分配。

    • 即使您想使用动态分配,也应该使用std::make_unique 而不是new
  • 您将 make_from_ints 定义为模板函数,它接受任意数量的 int 模板参数,但您没有给这些 intsa 名称 - 您永远不能使用它们!

  • 您的签名实际上被解析为 make_from_ints(int args, ...) - 这是一个与可变参数模板无关的 C va_args 签名。

    • 参数包的正确语法是type... name

如果您想接受与 模板参数推导 配合得很好的特定类型的任意数量的参数,最简单的方法是使用接受的常规 可变参数模板任意数量的类型和static_asserts 它们的类型(或使用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>{}...));

【讨论】:

  • &amp;&amp; ... 真的是字面意思吗?这是什么意思?
  • @BasileStarynkevitch:很抱歉没有解释 - 这是一个 C++17 折叠表达式。会改进我的答案。
  • 您可以使用std::conjunction,而不是折叠表达式,尽管std::conjunction 是C++ 17 的一个特性。我确实发现它比折叠表达式更容易阅读:std::conjunction&lt;std::is_same&lt;Ts, int&gt;...&gt;::value
猜你喜欢
  • 2015-05-27
  • 1970-01-01
  • 2014-09-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多