为什么 C++ 不支持强类型省略号?
Why doesn't C++ support strongly typed ellipsis?
有人可以向我解释为什么 C++,至少据我所知,没有实现强类型省略号函数,其效果如下:
void foo(double ...) {
// Do Something
}
意思是,通俗地说:'The user can pass a variable number of terms to the foo function, however, all of the terms must be doubles'
因为你可以使用
void foo(std::vector<T> values);
实现(某种程度上)您建议的方法是使用可变参数模板
template<typename... Arguments>
void foo(Arguments... parameters);
但是您现在可以在参数包中传递任何类型。
您提出的建议从未实施过,也许它可能是对语言的一个很好的补充,或者就目前的情况而言实施起来太困难了。您可以随时尝试撰写提案并将其提交给 isocpp.org
有
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
}
但是非类型模板参数(呃)的类型有一些限制:不能是double
,例如
可变参数模板和 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) {}
感谢 的 all_true
技巧。您还可以在 C++17 中使用折叠表达式。
随着以后的和即将发布的标准关注更简洁的语法(简洁的 for 循环、隐式函数模板...),您提出的语法很可能有一天会出现在标准中;)
具体为什么这样的事情没有被提出来(或者被提出来又被拒绝了),我也不知道。这样的事情肯定会有用,但会增加语言的复杂性。正如 所展示的那样,已经提出了一种使用模板实现此类功能的 C++11 方法。
当 Concepts 被添加到标准中时,我们将有另一种更简洁的方式:
template <Convertible<double>... Args>
void foo(Args... doubles);
或
template <typename... Args>
requires Convertible<Args, double>()...
void foo(Args... doubles);
或者,如 :
void foo(Convertible<double>... doubles);
就我个人而言,在当前的解决方案和我们将通过概念画板获得的解决方案之间,我认为这是解决问题的合适方案。特别是因为最后一个基本上是你最初要求的。
历史上,省略号语法 ...
来自 C。
这个复杂的野兽被用来驱动类似 printf
的函数,并且将与 va_list
、va_start
等一起使用...
如您所述,它不是类型安全的;但是 C 远非类型安全,对于任何指针类型,它从 void*
到 void*
的隐式转换,它对 integrals/floating 点值的隐式截断,等等......
因为 C++ 尽可能接近 C 的超集,所以它从 C 继承了省略号。
自诞生以来,C++ 实践不断发展,并且一直在大力推动更强大的类型化。
在 C++11 中,这最终导致:
- 初始化列表,给定类型的可变数量值的简写语法:
foo({1, 2, 3, 4, 5})
- 可变参数模板,它们本身就是一个野兽,允许编写类型安全的
printf
例如
可变参数模板实际上在其语法中重用了省略号 ...
,以表示 包 类型或值并作为解包运算符:
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...
在表达式中解压包
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)
{
}
基于:
void foo () {}
template <typename... Rest>
void foo (double arg, Rest... rest)
{
/* do something with arg */
foo(rest...);
}
如果使用 foo
的代码可以编译,您知道所有参数都可以转换为 double
。
有人可以向我解释为什么 C++,至少据我所知,没有实现强类型省略号函数,其效果如下:
void foo(double ...) {
// Do Something
}
意思是,通俗地说:'The user can pass a variable number of terms to the foo function, however, all of the terms must be doubles'
因为你可以使用
void foo(std::vector<T> values);
实现(某种程度上)您建议的方法是使用可变参数模板
template<typename... Arguments>
void foo(Arguments... parameters);
但是您现在可以在参数包中传递任何类型。 您提出的建议从未实施过,也许它可能是对语言的一个很好的补充,或者就目前的情况而言实施起来太困难了。您可以随时尝试撰写提案并将其提交给 isocpp.org
有
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
}
但是非类型模板参数(呃)的类型有一些限制:不能是double
,例如
可变参数模板和 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) {}
感谢 all_true
技巧。您还可以在 C++17 中使用折叠表达式。
随着以后的和即将发布的标准关注更简洁的语法(简洁的 for 循环、隐式函数模板...),您提出的语法很可能有一天会出现在标准中;)
具体为什么这样的事情没有被提出来(或者被提出来又被拒绝了),我也不知道。这样的事情肯定会有用,但会增加语言的复杂性。正如
当 Concepts 被添加到标准中时,我们将有另一种更简洁的方式:
template <Convertible<double>... Args>
void foo(Args... doubles);
或
template <typename... Args>
requires Convertible<Args, double>()...
void foo(Args... doubles);
或者,如
void foo(Convertible<double>... doubles);
就我个人而言,在当前的解决方案和我们将通过概念画板获得的解决方案之间,我认为这是解决问题的合适方案。特别是因为最后一个基本上是你最初要求的。
历史上,省略号语法 ...
来自 C。
这个复杂的野兽被用来驱动类似 printf
的函数,并且将与 va_list
、va_start
等一起使用...
如您所述,它不是类型安全的;但是 C 远非类型安全,对于任何指针类型,它从 void*
到 void*
的隐式转换,它对 integrals/floating 点值的隐式截断,等等......
因为 C++ 尽可能接近 C 的超集,所以它从 C 继承了省略号。
自诞生以来,C++ 实践不断发展,并且一直在大力推动更强大的类型化。
在 C++11 中,这最终导致:
- 初始化列表,给定类型的可变数量值的简写语法:
foo({1, 2, 3, 4, 5})
- 可变参数模板,它们本身就是一个野兽,允许编写类型安全的
printf
例如
可变参数模板实际上在其语法中重用了省略号 ...
,以表示 包 类型或值并作为解包运算符:
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...
在表达式中解压包
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)
{
}
基于
void foo () {}
template <typename... Rest>
void foo (double arg, Rest... rest)
{
/* do something with arg */
foo(rest...);
}
如果使用 foo
的代码可以编译,您知道所有参数都可以转换为 double
。