我试图从我的高级代码(使用 C++11)中消除原始指针,我发现引用(尤其是使用 const
)在许多情况下(当没有所有权转移时)是一个很好的替代品。
但是如果有怎么办变量 我想通过(const)引用传递的参数数量?
您不能创建 std::vector
或 std::initializer_list
的引用,因为这些容器在内部使用存储元素的地址,但引用没有地址。
一种可能性是使用 std::vector<std::reference_wrapper<T>>
但这需要笨拙的客户端代码,例如 doSomething({std::ref(A()), std::ref(B()), std::ref(C())})
而不是更好的 doSomething({A(), B(), C()})
哪里A
, B
和 C
是从 T
派生的类.
其他容器可以吗?或者也许使用可变参数模板?
最佳答案
使用可变参数模板函数来做到这一点。肯定没有必要通过T
的容器到一个函数来代替 T
的可变参数列表.
这是一个示例 C++11 程序:
#include <iostream>
#include <vector>
// Base case with 0 arguments
std::vector<int> foo() {
return std::vector<int>();
}
// General case with 1 + N arguments of type `int`.
// Return a `vector<int>` populated with the arguments.
template<typename ...Args>
std::vector<int> foo(int const & first, Args const &... rest)
{
std::vector<int> h(1,first);
if (sizeof...(rest)) {
std::vector<int> t = foo(rest...);
h.insert(h.end(),t.begin(),t.end());
}
return h;
}
struct bar{};
using namespace std;
int main()
{
int i = 1, j = 2, k = 3;
vector<int> v0 = foo(i,j);
vector<int> v1 = foo(i,j,k);
cout << v0.size() << endl;
cout << v1.size() << endl;
// bar b;
// vector<int> v2 = foo(i,j,k,b); <-- Compile error
return 0;
}
乍一看,似乎是
foo
的定义一般来说case 不限制所有参数为(可转换为)
int
,但实际上它们必须是 - 见证不可编译的初始化
v2
.继续回应 OP 的评论
如何编写一个类型安全的函数,接受任意给定的可变多个参数
类型,包括 const 引用类型,在 C++11 中不是一个有问题的问题。
核心语言为此提供了这种语法模式:
// Base case, 0 arguments
R Func() { [---] }
// General case, 1 + N arguments
template<typename U ...Args>
R Func(T [Modifier], Args [Modifier] ...args) {
[---]
if (sizeof...(args)) {
[---Func(args)---]
}
[---]
}
哪里
[---]
, [---Func(args)---]
可以填写。功能模板
foo
在上面的示例程序中应用了这种模式。你问:如果
foo
怎么办?有比创建容器更复杂的事情吗?答案是:无论复杂程度如何,您都可以适本地应用该模式
是 - 就像您应用该模式一样:
for( [---];[---];[---]) {
[---]
}
适本地,无论并发症是什么。可变参数函数模板
模式需要更多的习惯,因为它涉及递归模板
实例化 - 仅此而已。
你似乎混淆了两件事:
T 类型对象的可迭代序列。
在您自己的回答中,您说:
The function that takes variable number of arguments can be written as:
void foo(std::initializer_list<rvalue_reference_wrapper<Base>> args)
{
for (Base& arg : args)
{
arg.virtFunc();
doStuffWithBaseRef(arg);
}
}
那根本就不是 A),而是 B)。
在这里和在您的评论中,您都希望能够迭代
函数体内的可变参数函数的参数。在 C/C++ 中,有
不是迭代函数参数的机制,(除非它是
varargs function按照标准 C)
你还没有发明一个。如果函数是类型 B),那么显然
函数可以遍历
T
s 是 C
的成员那是函数的参数。这就是您在
foo
中所做的在你的回答中。如果在 C++ 中不可能对类型 A) 的函数进行编码,那么作为杂物
我们可以替换类型 B) 的函数。但是类型 A) 的函数是
使用所示的类型安全可变参数模板模式进行常规编码,没有这样的杂乱
被要求。如果您想要的是类型 A) 的函数,请使用该模式
并掌握它。如果你想要的是一个函数来迭代
T
序列的成员,然后照你做的做:写一个需要参数是
T
的可迭代序列.被认为是传递 [const] 的可迭代序列的一种可能方法
对函数的引用,您的解决方案具有禁用限制
这些引用只能是对临时对象的引用
在初始化列表中构造,而不是对预先存在的对象的引用
- 因为它们几乎总是在真正的代码中。例如,虽然
代码:
foo({Derived1(), Derived2()});
将使用
foo
的定义按预期编译和运行, Derived1
,Derived2
在您的回答中,更有可能的情况是:Derived1 d1; // <- Comes from somewhere
Derived2 d2; // <- Comes from somewhere
foo({d1,d2}); // <- Error
不会编译,因为左值
T
无法绑定(bind)到 T&&
.四处走走这个,你必须写:
Derived1 d1; // <- Comes from somewhere
Derived2 d2; // <- Comes from somewhere
foo({Derived1(d1),Derived2(d2)});
所以现在,您正在构建“参数”的临时拷贝,并且
initalizer_list
的 rvalue_reference_wrapper
s 的引用临时拷贝,以便您可以遍历对
foo
中的临时文件的引用。 .好吧,如果您必须使用“参数”的拷贝,那是多余的
费心构建对拷贝的引用序列。只是
将“参数”复制到任何合适的容器中并通过
foo
[const] 引用到那个。这不会停止
foo
从迭代 [const] 引用到容器成员就像现在一样。
你似乎部分地被这个问题锻炼了:什么
将是从多态派生的各种类型对象的合适容器
底座
B
,如果不是指向动态分配对象的原始指针的容器?对此毫无争议的答案是:
std::
容器 <std::shared_ptr<B>>
,其中 Container 是标准容器模板( vector 、列表等)
为您的应用程序提供适当的接口(interface)。更普遍的所谓
智能指针模板,
std::shared_ptr<T>
( documentation ) 和std::unique_ptr<T>
( documentation )是用于避免暴露原始动态指针的标准 C++11 资源。
您似乎也被
std::initializer_list
所吸引。为了将可迭代序列传递给函数,因为您可以轻松地
可以在使用时使用带支撑的初始化程序构造一个。那种便利
可以在不处理原始动态指针或智能指针的情况下保留。
例如。
void foo(std::initializer_list<std::shared_ptr<Base>> args)
{
for (auto arg : args)
{
arg->virtFunc();
doStuffWithBaseRef(*arg);
}
}
std::shared_ptr<Base> b1(new Derived1);
std::shared_ptr<Base> b2(new Derived2);
foo({b1,b2});
会好的,所以会:
void foo(std::initializer_list<Base *> args)
{
for (auto arg : args)
{
arg->virtFunc();
doStuffWithBaseRef(*arg);
}
}
Derived1 d1;
Derived2 d2;
foo({&d1,&d2});
关于c++ - 可变数量的(const)引用参数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23613363/