c++ - 可变数量的(const)引用参数

标签 c++ c++11

我试图从我的高级代码(使用 C++11)中消除原始指针,我发现引用(尤其是使用 const )在许多情况下(当没有所有权转移时)是一个很好的替代品。

但是如果有怎么办变量 我想通过(const)引用传递的参数数量?

您不能创建 std::vectorstd::initializer_list的引用,因为这些容器在内部使用存储元素的地址,但引用没有地址。

一种可能性是使用 std::vector<std::reference_wrapper<T>>但这需要笨拙的客户端代码,例如 doSomething({std::ref(A()), std::ref(B()), std::ref(C())})而不是更好的 doSomething({A(), B(), C()})哪里A , BC是从 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( [---];[---];[---]) {
    [---]
}

适本地,无论并发症是什么。可变参数函数模板
模式需要更多的习惯,因为它涉及递归模板
实例化 - 仅此而已。

你似乎混淆了两件事:
  • A) 接受可变数量的 T 类型参数的函数。
  • B) 一个接受一个 C 类型参数的函数,其中 C 是一个
    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_listrvalue_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/

    相关文章:

    c++ - 链接堆栈模板和一堆错误

    c++ - 将可变参数模板粘合到可变参数函数

    c++ - c++11 中首选的初始化方式

    C++:memcpy 是基于 POD 的子对象 UB 吗?

    c++ - 如何解压在 mixins 主机类中传递的一组元组(最终被转发给 mixins)

    c++ - 使用 lambda 函数签名的函数重载

    c++ - 如何在 excel 退出期间调试 c++ excel 插件的异常

    c++ - NaN 有 2 种 : signaling_NaN, Quiet_NaN,还有... NAN 本身?

    c++ - 为什么在 dlopen 函数中传递的 std::any 的 std::any_cast 会引发错误

    c++ - 在类类内不必要使用枚举类