为什么C ++不支持强类型省略号?

时间:2015-08-28 12:10:21

标签: c++ variadic-functions ellipsis

有人可以向我解释为什么C ++,至少据我所知,并没有实现强类型的省略号函数,这有点影响:

void foo(double ...) {
 // Do Something
}

这意味着,简单地说:'用户可以将可变数量的术语传递给foo函数,但是,所有术语必须是双倍的'

8 个答案:

答案 0 :(得分:23)

 void foo(std::initializer_list<double> values);
 // foo( {1.5, 3.14, 2.7} );

非常接近。

您也可以使用可变参数模板,但它会更具说话性。至于实际的原因,我会说引入新语法的努力可能不值得:你如何访问单个元素?你怎么知道什么时候停下来?是什么让它比std::initializer_list更好?

C ++确实有更接近的东西:非类型参数包。

template < non-type ... values>

喜欢

template <int ... Ints>
void foo()
{
     for (int i : {Ints...} )
         // do something with i
}

但是非类型模板参数(uhm)的类型有一些限制:例如,它不能是double

答案 1 :(得分:15)

历史上,省略号语法...来自C。

这个复杂的野兽用于驱动printf - 类似的功能,并与va_listva_start等一起使用...

如你所知,它不是类型安全的;但是C远不是类型安全的,它与void*对任何指针类型的隐式转换,它对积分/浮点值的隐式截断等等......

因为C ++作为C的超集尽可能接近,所以它继承了C的省略号。

自成立以来,C ++实践得到了发展,并且一直在努力推动更强的打字。

在C ++ 11中,这最终得到了:

  • 初始化列表,是给定类型的可变数量值的简写语法:foo({1, 2, 3, 4, 5})
  • 变量模板,它们是自己的野兽,允许编写类型安全的printf例如

Variadic模板实际上在语法中重用省略号...,表示类型或值的以及解包运算符:

void print(std::ostream&) {}

template <typename T, typename... Args>
void print(std::ostream& out, T const& t, Args const&... args) {
    print(out << t, args...); // recursive, unless there are no args left
                              // (in that case, it calls the first overload
                              // instead of recursing.)
}

请注意...的3种不同用途:

  • typename...声明可变参数类型
  • Args const&...声明一组参数
  • args...在表达式中解压缩包

答案 2 :(得分:11)

已经可以使用可变参数模板和SFINAE:

template <bool...> struct bool_pack;
template <bool... v>
using all_true = std::is_same<bool_pack<true, v...>, bool_pack<v..., true>>;

template <class... Doubles, class = std::enable_if_t<
    all_true<std::is_convertible<Doubles, double>{}...>{}
>>
void foo(Doubles... args) {}

感谢Columbo获得了很好的all_true技巧。您还可以在C ++ 17中使用折叠表达式。

随着后来且即将推出的标准专注于更简洁的语法(简洁的循环,隐式函数模板......),您提出的语法很可能在标准的一天内结束;)

答案 3 :(得分:2)

为什么没有提出(或提议和拒绝)这样的事情,我不知道。这样的事情肯定会有用,但会增加语言的复杂性。正如Quentin所示,已经提出了一种用模板实现这种事情的C ++ 11方法。

当Concepts添加到标准中时,我们将采用另一种更简洁的方式:

template <Convertible<double>... Args>
void foo(Args... doubles);

template <typename... Args>
    requires Convertible<Args, double>()...
void foo(Args... doubles);

或,@dyp points out

void foo(Convertible<double>... doubles);    

就个人而言,在目前的解决方案和我们将通过Concepts获得的解决方案之间,我觉得这是解决问题的适当方法。特别是因为最后一个基本上是你最初要求的。

答案 4 :(得分:1)

实现(排序)你建议的方法是使用可变参数模板

template<typename... Arguments>
void foo(Arguments... parameters);

但是你现在可以在参数包中传递任何类型。 你提出的建议从来没有实现过,也许它可能是语言的一个很好的补充,或者它可能只是太难以实现。您可以随时尝试撰写提案并将其提交给isocpp.org

答案 5 :(得分:1)

template<typename T, typename... Arguments>
struct are_same;

template <typename T, typename A1, typename... Args>
struct are_same<T, A1, Args...>{    static const bool value = std::is_same<T, A1>::value && are_same<T, Args...>::value;};

template <typename T>
struct are_same<T>{static const bool value = true;};

template<typename T, typename... Arguments>
using requires_same = std::enable_if_t<are_same<T, Arguments...>::value>;

template <typename... Arguments, typename = requires_same<double, Arguments...>>
void foo(Arguments ... parameters)
{
}

答案 6 :(得分:1)

基于Matthew's answer

void foo () {}

template <typename... Rest>
void foo (double arg, Rest... rest)
{
    /* do something with arg */
    foo(rest...);
}

如果使用foo编译的代码,您知道所有参数都可以转换为double

答案 7 :(得分:0)

因为你可以使用

module FaceData