(const)参考变量的数量可变 [英] Variable number of (const) reference arguments
问题描述
我试图从高级代码中消除原始指针(使用C ++ 11),并且发现引用(尤其是使用 const
)是一个很好的选择
I'm trying to eliminate raw pointers from my high-level code (using C++11) and I found references (especially with const
) to be a good replacement in many cases (when there is no ownership transfer).
但是如果我有变量个参数,该怎么办?想要通过(常量)引用?
But what to do if there is variable number of arguments that I'd like to pass by (const) reference?
您无法创建 std :: vector
或 std :: initializer_list
引用,因为这些容器内部使用存储元素的地址,但引用没有地址。
You can't create an std::vector
or std::initializer_list
of references as these containers internally use the address of the stored elements but references don't have an address.
一种可能性是使用 std :: vector< std :: reference_wrapper< T>>
,但这需要笨拙的客户端代码,例如 doSomething({std :: ref(A()),std :: ref(B()),std :: ref(C())})
而不是更好的 doSomething({A(),B(),C()})
其中 A
, B
和 C
是从 T
派生的类。
A possibility would be to use std::vector<std::reference_wrapper<T>>
but that would require awkward client code like doSomething({std::ref(A()), std::ref(B()), std::ref(C())})
instead of the nicer doSomething({A(), B(), C()})
where A
, B
and C
are classes derived from T
.
是否可以使用其他容器?还是可能使用可变参数模板?
Is it possible with some other container? Or maybe with variadic templates?
推荐答案
使用可变参数模板功能来执行此操作。当然没有必要将 T
的容器传递给函数,以代替 T的变量参数列表。
。
Use a variadic template function to do this. There is certainly no need to pass a container of T
to a function as a substitute for a variable argument list of T
.
下面是一个示例C ++ 11程序:
Here is an an example C++11 program:
#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 并不约束所有参数都是(可转换为)
int
,
,但实际上它们必须是-见证
v2
的不可编译的初始化。
At a glance, it may appear that the definition of foo
in the general
case does not constraint all arguments to be (convertible to) int
,
but in fact they must be - witness the uncompilable initialialization of
v2
.
OP的注释
如何编写一个类型安全的函数,该函数采用任意给定的
类型(包括const引用类型)的各种参数作为变量C ++ 11中的问题。
核心语言为此提供了这种语法模式:
How to write a type-safe function taking variably many arguments of any given type, including const reference types, is not a problematic question in C++11. The core language provides this syntactical pattern for doing it:
// 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
除了创建容器之外,还有什么更复杂的事情吗?
答案是:无论采用
是什么并发症,您都应适当应用模式-就像应用模式一样:
The function template foo
in the example program above applies this pattern.
You ask: What if foo
does something more complicated than just create a container?
The answer is: You apply the pattern appropriately, whatever the complications
are - just as you would apply the pattern:
for( [---];[---];[---]) {
[---]
}
适当地考虑并发症。可变参数函数模板
模式需要习惯一些,因为它涉及递归模板
实例化-就是这样。
appropriately, whatever the complications are. The variadic function template pattern takes a bit more habituation because it involves recursive template instantiation - that's all.
您似乎在混淆两件事:
- A)接受可变数量T类型参数的函数。
- B)接受一个类型为C的参数的函数,其中C是类型为T的对象的
可迭代序列。
在您自己的答案中,您说:
In your own answer you say:
采用可变数量参数的函数可以写为:
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)。
That simply is not an A), it is a B).
在这里和您的注释中,您都希望能够对
参数进行迭代功能体内的可变参数功能。在C / C ++中,没有
可以迭代函数的参数的机制(除非它是
a varargs 函数(按标准C)
,而您还没有发明一个。如果函数的类型为B),则显然
函数可以迭代成员的
是函数的参数。 T
s c $ c> C
这就是您在答案中的 foo
中所做的事情。
Both here and in your comments you exhibit a wish to be able to iterate over the
arguments of a variadic function within the function body. In C/C++, there
is no mechanism to iterate over the arguments of a function, (unless it is
a varargs function per Standard C)
and you have not invented one. If a function is of type B), then obviously the
function can iterate over the T
s that are members of the C
that is the argument of the function.
That is what you are doing in foo
in your answer.
如果在C ++中是不可能的来编码类型A)的函数,然后作为
的替代,我们可以替换类型B)的函数。但是类型A)的函数是使用所示的类型安全可变参数模板模式常规编码的
,因此不需要这样的kbudge
。如果您想要的是类型A)的函数,请使用该模式
并挂起它。如果您想要的是一个对序列 T
的
成员进行迭代的函数,请按照您所要做的操作:编写一个需要$ b $的函数b参数,它是 T
的可迭代序列。
If it was impossible in C++ to code functions of type A) then as a kludge
we could substitute functions of type B). But functions of type A) are
routinely coded using the type-safe variadic template pattern shown and no such kludge
is called for. If what you want is a function of type A), use that pattern
and get the hang of it. If what you want is a function to iterate over the
members of a sequence of T
, then do as you have done: write one that takes an
argument that is an iterable sequence of T
.
被认为是传递可迭代对象的可能手段对函数的[const]
引用序列,您的解决方案具有禁用限制
,即这些引用只能是对是
的临时对象的引用,初始化程序列表,而不是引用先前存在的对象
,因为它们几乎总是真实的代码。因此,例如,在
代码中:
Considered as a possible means for passing an iterable sequence of [const] references to a function, your solution has the disabling limitation that those references can only be references to temporary objects that are constructed in the initializer list, not references to pre-existing objects - as they almost always would be in real code. So for example, while the code:
foo({Derived1(), Derived2()});
将使用 foo $ c的定义按预期运行并运行$ c>,
Derived1
,
Derived2
在您的答案中,可能性更大:
will compile and run as expected with the definitions of foo
, Derived1
,
Derived2
in your answer, the vastly more likely case:
Derived1 d1; // <- Comes from somewhere
Derived2 d2; // <- Comes from somewhere
foo({d1,d2}); // <- Error
将不会编译,因为左值 T
无法绑定到 T&
。要解决
的问题,您必须编写:
will not compile, because an lvalue T
cannot bind to a T&&
. To get around
this, you must write:
Derived1 d1; // <- Comes from somewhere
Derived2 d2; // <- Comes from somewhere
foo({Derived1(d1),Derived2(d2)});
因此,现在,您正在构造参数的临时副本 ,以及
和
临时副本引用的 rvalue_reference_wrapper
的斜体列表
,您可以遍历 foo
中对临时变量的引用。
so that now, you are constructing temporary copies of the "arguments", and
an initalizer_list
of rvalue_reference_wrapper
s of references to the
temporary copies, so that you can iterate over references to the temporaries within foo
.
好吧,如果您必须使用参数,那么麻烦的是构造一个对副本的引用序列。只需
将参数复制到任何合适的容器中,然后将 foo
一个[const]引用
传递给该容器。这不会阻止 foo
像现在那样遍历[const]对
容器成员的引用。
Well, if you have to use copies of the "arguments", it is superfluous
to bother with constructing a sequence of references to the copies. Just
copy the "arguments" into any suitable container and pass foo
a [const] reference
to that. This won't stop foo
from iterating over [const] references to the
container members just as it does now.
问题似乎使您受到部分困扰:
将是一个适合容器的容器,用于容纳从多态
基数衍生的各种类型的对象 B
,如果不包含指向动态分配对象的原始指针的容器?
It seems likely that you are exercised in part by the question: What
would be a suitable container for objects of varied types derived from a polymorphic
base B
, if not a container of raw pointers to dynamically allocated objects?
无争议答案是: std ::
容器 < std :: shared_ptr< B>>
,
,其中 Container 是一个标准容器模板(矢量,列表等),
为您的应用程序提供了适当的接口。更一般地,所谓的
智能指针模板, std :: shared_ptr< T>
(文档)和
std :: unique_ptr< T>
(文档)
是标准C ++ 11资源,避免暴露原始动态指针。
An uncontroversial answer to that is: std::
Container<std::shared_ptr<B>>
,
where Container is a Standard container template (vector,list, etc.) that
provides an appropriate interface to your application. More generally the so-called
smart pointer templates, std::shared_ptr<T>
(documentation) and
std::unique_ptr<T>
(documentation)
are the standard C++11 resources for avoiding exposure of raw dynamic pointers.
似乎您也很可能被 std :: initializer_list $ c所吸引$ c>为
传递一个可迭代的序列到一个函数,因为
可以很容易地在支撑点构造器的使用点构造一个可迭代的序列。这种便利
可以保留,而无需处理原始动态指针或智能指针。
Eg
It seems likely also that you are attracted to std::initializer_list
for
passing an iterable sequence to a function because of the ease with which you
can construct one at the point of use with a braced initializer. That convenience
can be retained without dealing in raw dynamic pointers or smart pointers.
E.g.
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});
这篇关于(const)参考变量的数量可变的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!