我可以在不使用原始指针的情况下避免在 std::initializer_list 初始化期间进行复制吗?

Can I avoid copying during the intialization of a std::initializer_list without using raw pointers?

假设我在本地声明了几个对象,我想使用基于范围的 for 语法对其进行迭代。这似乎运作良好,但是,似乎要将本地对象放入 initializer_list,执行复制。这对于像 std::shared_ptr 这样的对象来说是个坏消息,据我所知,增加引用计数是一个原子操作。我认为唯一可以避免这种情况的方法是使用原始指针。

#include <iostream>
#include <memory>

int main() {
    std::shared_ptr<int> ptrInt1 = std::make_shared<int>(1);
    std::shared_ptr<int> ptrInt2 = std::make_shared<int>(2);
    /* in this loop, ptrInt1 and ptrInt2 are copied before they are binded
       to ptrInt, this is ugly since the reference counter needs to temporarily
       increased */
    for(const std::shared_ptr<int>& ptrInt : {ptrInt1, ptrInt2}) {
        std::cerr << *ptrInt << std::endl;
    }
    /* this solution works, but it feels somewhat ugly having to convert my smart
       pointers to raw pointers to avoid the copying, perhaps there is a better
       solution ?? */
    for(const int* rawPtrInt : {ptrInt1.get(), ptrInt2.get()}) {
        std::cerr << *rawPtrInt << std::endl;
    }
    return 0;
}

有没有办法在不复制或诉诸使用原始指针的情况下迭代一组本地声明的对象?

不幸的是,std::initializer_list 不适合这项任务。自

The underlying array is a temporary array of type const T[N], in which each element is copy-initialized...

(https://en.cppreference.com/w/cpp/utility/initializer_list),它会执行复制,编译器不会省略它们。

为了这个任务,我会做其他事情。可能创建一个编译时可变参数模板 class,模板化了指向底层对象的指针。如果您需要一些代码,请告诉我。

您可以使用 std::ref 构建 std::reference_wrapper 的列表。这会隐藏指针并让您像

这样编写列表
for(const std::shared_ptr<int>& ptrInt : {std::ref(ptrInt1), std::ref(ptrInt2)}) {
    std::cerr << *ptrInt << std::endl;
}

这是一个扩展 并减少了一些输入的小函数模板。

#include <array>
#include <functional>

// No rvalues, thanks to @NathanOliver for pointing that out:
template <class ...T>
auto crefRange(const T&&...) = delete;

template <class ...T>
auto crefRange(const T&... args)
{
   using First = std::tuple_element_t<0, std::tuple<T...>>;

   return std::array<First, sizeof...(T)>{{std::cref(args)...}};
}

可以通过

实例化调用
for(const std::shared_ptr<int>& ptrInt : crefRange(ptrInt1, ptrInt2))
    std::cerr << *ptrInt << std::endl;

如果您使用不同的类型实例化它,这将失败,但此限制与 std::initializer_list 方法相同。