为什么std::initializer_list会复制项目?

4

我一直以为std::initializer_list是一个轻量级的代理对象,只会从列表项中获取const引用而不是复制它们。 但后来我发现,在这种情况下实际上是执行了复制:

struct Test {
    Test() { 
        std::cout << this << " default ctor" << std::endl;
    }
    Test(const Test&) {
        std::cout << this << " copy ctor" << std::endl;
    }
    ~Test() {
        std::cout << this << " destructor" << std::endl;
    }
};

int main() {
    Test a;
    Test b;
    Test c;
    std::cout << "for begin" << std::endl;
    for(const auto& current : {a, b, c}) {
        std::cout << "Current: " << &current << std::endl;
    }
    std::cout << "for end" << std::endl;
}

上述代码的输出:
0x63e5acda default ctor
0x63e5acdb default ctor
0x63e5acdc default ctor
for begin
0x63e5acdd copy ctor
0x63e5acde copy ctor
0x63e5acdf copy ctor
Current: 0x63e5acdd
Current: 0x63e5acde
Current: 0x63e5acdf
0x63e5acdf destructor
0x63e5acde destructor
0x63e5acdd destructor
for end
0x63e5acdc destructor
0x63e5acdb destructor
0x63e5acda destructor

为什么在这种情况下std::initializer_list会复制项目,而不只是获取它们的引用?是否有类似于for(auto&& x : {a, b, c})的优雅写法,但不复制现有项目?


我很好奇如何实现这个。楼主,你能否更新你的问题并寻求解决方案呢? - SwiftMango
@texasbruce {&a, &b, &c} 会避免复制。 - M.M
@M.M,那依然是指针值的拷贝。 显然,initializer_list总是实例化一个新的实例。 - SwiftMango
@texasbruce 复制指针值没有问题,它可能会生成与迭代“引用列表”相同的代码。 - M.M
从下面的答案中可以看出,std::initializer_list有一个底层数组,因此似乎{&a, &b, &c}是唯一可行的解决方案...或者另外一种类似于some_custom_reference_holding_struct{a, b, c}的方法也可能有效,但它们可能太长或太丑了,不好使用 :/ - LanYi
1
@LanYi,你可以使用std::ref,像这样{ref(a), ref(b), ref(c)}。它会返回一个reference_wrapper。但是为了获得最佳性能,指针可能是最好的选择。我认为这值得向委员会提出并看看是否有更优雅的解决方案。 - SwiftMango
1个回答

4
根据std::initializer_list文档:
底层数组是const T[N]类型的临时数组,其中每个元素都从原始初始化列表的相应元素进行复制初始化(除了缩小转换无效)。底层数组的生命周期与任何其他临时对象相同,除了从数组初始化initializer_list对象会像绑定到临时对象的引用一样延长数组的生命周期(具有相同的例外情况,例如用于初始化非静态类成员)。底层数组可能分配在只读内存中。

哎呀,没有注意到 std::initializer_list 有一个底层数组... 看起来我只能做 {&a, &b, &c} 来避免拷贝... - LanYi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接