是否可以在模板外部使用 c++11 参数包
Can c++11 parameter packs be used outside templates?
我想知道我是否可以拥有由单个明确指定的类型组成的参数包。例如,像这样:
#include <iostream>
using namespace std;
void show() { }
template<typename First, typename... Rest>
void show(First f, Rest... rest)
{
cout << f << endl;
show(rest...);
}
void foo(int f, int... args) // error
{
show(f, args...);
}
int main()
{
foo(1, 2, 3);
}
我遇到的问题是foo()
的定义。使用OS X clang++版本5(llvm 3.3svn(,我收到错误error: type 'int' of function parameter pack does not contain any unexpanded parameter packs
。
当然,我可以通过更改为foo()
函数模板来编译它:
template<typename... Args>
void foo(int f, Args... args)
{
show(f, args...);
}
但是现在foo()
将接受第一个参数的int
,其余参数的任何输出都可以流式传输。例如:
struct x { };
ostream& operator<<(ostream& o, x)
{
o << "x";
return o;
}
int main()
{
foo(1, 2, x(), 3); // compiles :(
}
现在,我在这里看到了公认的解决方案,它建议使用类型特征和std::enable_if
,但这很麻烦。他们还建议使用std::array
但我认为一个简单的std::initializer_list
工作得很好,看起来更干净,如下所示:
void foo_impl(initializer_list<int> ints)
{
for(int i: ints)
cout << i << endl;
}
template<typename... Args>
void foo(int f, Args... args)
{
foo_impl({f, args...});
}
struct x { };
ostream& operator<<(ostream& o, x)
{
o << "x";
return o;
}
int main()
{
foo(1, 2, 3);
foo(1, 2, x(), 3); // no longer compiles
// we also get an error saying no known conversion from 'x' to 'int' :)
}
所以这很整洁。但问题仍然存在,这有必要吗?真的没有办法定义接受特定类型的参数包的非模板函数吗?喜欢这个:
void foo(int... args) { }
void foo(int... args) {}
不,你不能写那个。
但是这种方法可以产生相同的效果:
template<typename ...Ints>
void foo(Ints... ints)
{
int args[] { ints... }; //unpack ints here
//use args
}
使用此方法,如果需要,可以传递所有int
。如果传递给foo
的任何参数不能int
或转换为int
,上面的代码将导致编译错误,如果允许的话,int ...args
方法
如果您想要这种行为,您还可以使用 static_assert
来确保所有Ints
确实int
:
template<typename ...Ints>
void foo(Ints... ints)
{
static_assert(is_all_same<int, Ints...>::value, "Arguments must be int.");
int args[] { ints... }; //unpack ints here
//use args
}
现在你必须实现is_all_same
元函数,这并不难实现。
好的,这是基本思想。您可以使用可变参数模板以及一些实用元函数和帮助程序函数的帮助编写更复杂的代码。
对于你可以用可变参数做的很多工作,你甚至不需要存储在数组args[]
例如,如果你想打印参数来std::ostream
,那么你可以这样做:
struct sink { template<typename ...T> sink(T && ... ) {} };
template<typename ...Ints>
void foo(Ints... ints)
{
//some code
sink { (std::cout << ints)... };
}
在这里,您将创建一个类型为 sink
的临时对象,以便使用列表初始化语法解压缩模板参数。
最后,您可以使用std::initializer_list<int>
本身:
void foo(initializer_list<int> const & ints)
{
}
或者std::vector<int>
以防您需要在 foo()
内类似矢量的行为。如果使用其中任何一个,则必须在调用函数时使用 {}
:
f({1,2,3});
这可能并不理想,但我认为随着 C++11 的出现,您将非常频繁地看到这样的代码!
与布莱恩的回答一样,我意识到这最初是为 C++11 设计的,但在 C++20 中,这可以使用概念以非常简单的方式解决:
#include <concepts>
void f(std::integral auto... ints)
{
// ...
}
std::integral
接受任何整数类型,所以如果可以接受的话,它更通用一些。如果没有,您可以执行以下操作:
#include <concepts>
template<class T>
concept exactly_int = std::same_as<int,T>;
void f(exactly_int auto... ints)
{
// ...
}
为了对此添加更多解释,auto
本质上是一个隐式模板,它之前的名称限制了允许的类型。所以在第一个例子中,任何满足std::integral
(int
、long
、unsigned
、char
等(的东西都是允许的。第二个只允许int
,因为这是唯一满足所定义概念的类型。
还有一种更简单的方法可以做到这一点:概念在用作约束时,使用受约束的类型作为其第一个参数,因此您可以简单地编写:
#include <concepts>
void f(std::same_as<int> auto... ints)
{
// ...
}
为什么foo_impl
解决方法,而不仅仅是直接在foo
的签名中使用initialize_list<int>
? 它阐明了您接受所述类型的可变大小参数列表。
您可以指定要显示的类型:
#include <iostream>
template<typename T>
void show_type() {}
template<typename T, typename... Rest>
void show_type(const T& x, Rest... rest)
{
std::cout << x << std::endl;
show_type<T>(rest...);
}
template<typename... Args>
void foo(int x, Args... args)
{
show_type<int>(x, args...);
}
struct X { };
std::ostream& operator<<(std::ostream& o, X)
{
o << "x";
return o;
}
int main()
{
foo(1, 2, 3);
foo(1, 2, 3.0); // Implicit conversion
// or just
show_type<int>(1, 2, 3);
foo(1, 2, X()); // Fails to compile
}
我意识到这被标记为 C++11,但 C++17/1z 的功能在这里非常有效,所以我认为它的解决方案值得发布:
template<typename... Ints>
void foo(Ints... xs)
{
static_assert(std::conjunction<std::is_integral<Ints>...>::value);
(std::cout << xs << 'n', ...);
}
你不能使用这样的模板
<MyType i, MyType ... myTypes>
对于函数,但你猫将其用于类/结构。只需将您的函数更改为静态结构成员即可完成(求和示例(:
template<int i, int ... ints>
struct A <i, ints...> {
static int Do() {
return i + A<ints...>::Do();
}
};
这不是所有要做的工作 - 还需要边界结构。所有示例(使用 Show(( 方法(:
#include <iostream>
using namespace std;
template<int ...> // declaration needed
struct A {};
template<int i> // boundary
struct A <i>{
static void Show() {
cout << i << endl;
}
};
template<int i, int ... ints> // main struct
struct A <i, ints...> {
static void Show() {
cout << i << endl;
A<ints...>::Show();
}
};
int main() {
A<5, 45, 2, -100>::Show();
cout << endl;
A<-15, -3, 2>::Show();
return 0;
}
- 在 c++11 中为 pthread 设置调度参数
- 为什么 c++11 std::lock 和 std::scoped_lock 至少需要 2 个参数?
- 在 C++11 中获取函子作为参数
- C++/11 auto 关键字是在更有效时推导参数进行按引用传递,还是始终按值传递?
- 如何捕获函数参数并存储函数指针以供以后在 C++11 中执行?
- C++11:无法推断模板参数
- 带有void类型和参数的C++11模板专用化
- 使用 C++11 将指向成员函数的指针作为参数传递
- 遍历地图并将该对用作参考参数 C++11
- 发生 Qt5.11 应用插件构建错误:信号和插槽参数不兼容
- 网站组装 |错误:'C/ObjC'不允许使用无效'-std=c++11'参数
- C++11:参数包扩展计数器
- 是否可以在模板外部使用 c++11 参数包
- C++11 参数包重载
- c ++ 11 参数包在 Apple LLVM 7.0.0 中存在错误行为,但适用于 GCC-5.1
- gcc-4.8(和4.9)未识别-std=c++11参数
- C++11参数子句中声明符和抽象声明符之间的歧义
- 如何获取c++ 11参数包中的类型
- 可以将c++ 11参数传递给built_root配置
- C++11 "T&&"参数失去其"&&"正确的术语?