当前位置: 首页 > 知识库问答 >
问题:

为什么我不能保证复制省略std:tuple?

尹雅健
2023-03-14

我希望在C++20中,下面的代码在打印A和B之间不打印任何内容(因为我希望保证RVO会起作用)。但输出为:

a

拜拜

b

c

拜拜

拜拜

所以大概是创建了一个临时的。

#include<iostream>
#include<tuple>
struct INeedElision{
    int i;
    ~INeedElision(){
        std::cout << "Bye\n";
    }
};

std::tuple<int, INeedElision> f(){

    int i = 47;
    return {i, {47}};
}

INeedElision g(){

    return {};
}

int main()
{   
    std::cout << "A\n"; 
    auto x = f();
    std::cout << "B\n";
    auto y = g();
    std::cout << "C\n";
}

这种行为的原因是什么?是否有避免复制(不使用指针)的变通方法?

https://godbolt.org/z/zasogd

共有2个答案

桂杰
2023-03-14

只有返回对象本身时,才会获得复制选择:

std::vector<int> fn1()
{
   return std::vector<int>{}; // guaranteed copy elision
}

std::vector<int> fn2()
{
   std::vector<int> vec;
   return vec; // a good compiler will manage to elide the copy/move here
}

在您的示例中,您返回的是tuple,因此tuple本身可能被复制省略,但传递给tuple构造函数的参数却不被复制省略!

std::tuple<int, INeedElision> f(){

    int i = 47;
    return {i, {47}}; // construct the tuple in place of the return address but the arguments are copied into the tuple and not even moved ! to move call std::move explicitly
}

编译器不允许删除传递给元组构造函数的参数副本,因为您返回的不是参数本身,而是包含这些参数副本的元组。还要注意,表不能保存对参数的引用,因为这些局部变量在函数返回时已经被销毁,从而产生悬空引用。

如果您想在C++17中获得一个复制省略的机会,并在以后做一些类似的事情:

std::tuple<int, INeedElision> f(){

    std::tuple<int, INeedElision> ret;
    auto& [i, ne] = ret;
    i = 47;
    ne = 47;
    return ret;
}
徐俊楚
2023-03-14

{i,{47}}构造std::tuple 时,std::tuple的选定构造函数通过对const的lvalue-reference获取元素。

tuple( const Types&... args );

然后,当使用{i,{47}}作为初始值设定项时,将构造一个临时的ineDelision,然后传递给std::tuple的构造函数(并得到复制)。临时对象将立即被销毁,您将看到“a”和“b”之间的“bye”。

BTW:std::tuple的第三个构造函数不会用于这种情况。

template< class... UTypes >
tuple( UTypes&&... args );

它是一个构造函数模板,像{47}这样的括号-init-list没有类型,不能通过模板参数推导来推导。

另一方面,如果ineDelision有一个转换构造函数,它取int,并使初始值设定项为{i,47},则将使用std::tuple的第三个构造函数,而不构造临时的ineDelision;该元素将从int47就地构造。

活着

 类似资料:
  • 我需要在Go中复制一个切片,并读取文档。有一个复制功能可供我使用。 copy内置函数将元素从源片复制到目标片。(作为一种特殊情况,它还会将字节从字符串复制到字节片。)源和目标可能重叠。Copy返回复制的元素数,它是len(src)和len(dst)中的最小值。 但当我这样做的时候: 我的和以前一样是空的(我甚至尝试使用): 你可以去游乐场看看。那么为什么我不能复制一个切片呢?

  • 问题内容: 从JavaDoc: 验证此容器及其所有子组件。验证容器意味着布置其子组件。 那就是我想做的。使用尽可能轻巧的组件。但是当我这样做时,对的调用不会使组件“有效”。 为什么我不能做一个有效的? 问题答案: 在文档中说: 当组件在其父容器中的大小和位置正确且其所有子组件也均有效时,该组件才有效。 这种情况,直到你达到顶级容器(,或)。在您所遇到的问题的示例中,您没有父母,所以它永远不会有效。

  • 下面是一个多态字段: 其中是接口,是结构/类中其他位置的字段。 在这种情况下我怎么会有这种行为呢?

  • 问题内容: 我有一个div,在该div内,我有一个无序列表。我已将其应用于无序列表,但不会在标头div中居中。 有人可以告诉我为什么吗?我认为,如果我定义父div的宽度,那么无序列表应该能够以居中。我想念什么? 这是我的代码: 问题答案: 您需要定义要居中的元素的宽度,而不是父元素。 编辑 :好的,我现在已经看到了测试页,这就是我认为您想要的:

  • 主要内容:可见性,原子性,举个例子首先要了解的是,volatile可以保证可见性和顺序性,这些都很好理解,那么它为什么不能保证原子性呢? 可见性 可见性与Java的内存模型有关,模型采用缓存与主存的方式对变量进行操作,也就是说,每个线程都有自己的缓存空间,对变量的操作都是在缓存中进行的,之后再将修改后的值返回到主存中,这就带来了问题,有可能一个线程在将共享变量修改后,还没有来的及将缓存中的变量返回给主存中,另外一个线程就对共享变量

  • 主要内容:可见性,原子性,举个例子首先要了解的是,volatile可以保证可见性和顺序性,这些都很好理解,那么它为什么不能保证原子性呢? 可见性 可见性与Java的内存模型有关,模型采用缓存与主存的方式对变量进行操作,也就是说,每个线程都有自己的缓存空间,对变量的操作都是在缓存中进行的,之后再将修改后的值返回到主存中,这就带来了问题,有可能一个线程在将共享变量修改后,还没有来的及将缓存中的变量返回给主存中,另外一个线程就对共享变量

  • 问题内容: 我需要在Go中制作切片的副本,并阅读文档,这里有一个复制功能供我使用。 内置复制功能将元素从源切片复制到目标切片。(在特殊情况下,它还会将字节从字符串复制到字节切片。)源和目标可能会重叠。复制返回复制的元素数量,该数量将是len(src)和len(dst)的最小值。 但是当我这样做时: 与以前一样,我是空的(甚至尝试使用): 您可以在运动场上查看。那为什么不能复制切片? 问题答案: 内

  • 我有自己的目录结构: 在我的Dockerfile中,我将复制到目录,但不幸的是,它没有被复制: 错误: