(const)参考变量的数量可变 [英] Variable number of (const) reference arguments

查看:49
本文介绍了(const)参考变量的数量可变的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图从高级代码中消除原始指针(使用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 Ts 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 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_wrappers 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
传递一个可迭代的序列到一个函数,因为
可以很容易地在支撑点构造器的使用点构造一个可迭代的序列。这种便利
可以保留,而无需处理原始动态指针智能指针。
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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆