标签: copy-elision

如何使用 std::variant 保证复制省略?

我有这种类型:

\n\n
struct immobile {\n   // other stuff omitted\n   immobile(immobile&) = delete;\n   immobile(immobile&&) = delete;\n};\nimmobile mk_immobile();\n// e.g. this compiles\n// mk_immobile() is a prvalue and i is its result object\nimmobile i(mk_immobile());\n
Run Code Online (Sandbox Code Playgroud)\n\n

我也有这个类模板:

\n\n
template<typename T>\nstruct container {\n    std::variant<T, other_stuff> var;\n    template<typename... Args>\n    container(Args&&... args)\n    : var(std::in_place_index<0>, std::forward<Args>(args)...) {}\n};\n
Run Code Online (Sandbox Code Playgroud)\n\n

我想container围绕 生成的对象构造mk_immobile(),该immobile对象用于初始化 的变体之一var

\n\n
container<immobile> c(mk_immobile());\n
Run Code Online (Sandbox Code Playgroud)\n\n

然而,这是行不通的。其一,std::variant的构造函数要求std::is_constructible_v<immobile, immobile>,但它不成立。更糟糕的是,即使这个简化版本也失败了:

\n\n
template<typename T>\nstruct demonstration {\n    T t;\n    template<typename... Args>\n    demonstration(Args&&... …
Run Code Online (Sandbox Code Playgroud)

c++ variant copy-elision c++17

6
推荐指数
1
解决办法
303
查看次数

函数参数按值传递比按引用传递更快?

我接受了一次采访,得到了以下函数声明:

int f1(const std::vector<int> vec)
Run Code Online (Sandbox Code Playgroud)

我建议我们不要复制向量,而应该使用 const 引用(更不用说 const 复制没有多大意义),但面试官声称编译器复制省略会处理它。我当场想不出什么强有力的论据,但今天我做了一些研究。

我实现了以下两个简单的示例函数:

int f1(const std::vector<int> vec) {
    const auto num = vec.size();
    return num * num;
}


int f2(const std::vector<int>& vec) {
    const auto num = vec.size();
    return num * num;
}
Run Code Online (Sandbox Code Playgroud)

godbolt汇编中可以清楚地看出,该f2函数有 2 条附加指令,因此它应该更慢。(我认为 2mov在现代 CPU 中技术上几乎是免费的)

我还使用quick-bench来测量这两个解决方案,但它证实了我的怀疑,通过const-ref更快。(即使对于 1 个元素)

在此输入图像描述

我怀疑可能因为 的原因而不允许复制省略benchmark::DoNotOptimize(result);,但删除它后,我收到了类似的结果。

现在我有了这些结果,但我觉得还是不够有说服力。

你怎么认为?

对于使用其中一种而不是另一种,您有什么好的理由吗?

c++ optimization copy-elision

6
推荐指数
2
解决办法
346
查看次数

在c ++ 11中,虚函数可以通过移动语义有效地返回一个大值吗?

通常,这将被优化为不涉及复制大值(因为std::vector已启用移动语义):

std::vector<int> makeABigThing(){
    std::vector<int> large_thing(1000, 0);
    return large_thing;
}
Run Code Online (Sandbox Code Playgroud)

如果函数是虚方法,也可以以相同的方式优化:

struct Foo{
    virtual std::vector<int> makeABigThing(){
        std::vector<int> large_thing(1000, 0);
        return large_thing;
    }
};
Run Code Online (Sandbox Code Playgroud)

即,即使在运行时选择被调用函数,移动语义也能正常工作吗?

c++ virtual move-semantics copy-elision c++11

5
推荐指数
1
解决办法
254
查看次数

std :: pair移动没有在定义上省略?

我注意到Visual Studio 2012中出现了一些非常奇怪的事情:定义一对像这样的对象:

    auto objp = pair<int, LogMe>();
Run Code Online (Sandbox Code Playgroud)

不会的Elid一对在VC11的复制/移动,这个调用会打印:

LogMe::LogMe - def.ctor!
LogMe::LogMe - move.ctor!
LogMe::~LogMe - dtor!
Run Code Online (Sandbox Code Playgroud)

也就是说,创建一个临时对,然后其移动到objp变量中.(声明pair<...> obj;它只记录默认的ctor)

我单独使用LogMe测试对象进行交叉检查:

cout << "# Construct Object via auto obj = ...\n";
auto obj = LogMe();

# Construct Object via auto obj = ...
LogMe::LogMe - def.ctor!
Run Code Online (Sandbox Code Playgroud)

这里的任务将被删除.

这似乎是VC11特有的,因为在IDEOne(使用gcc 4.8.1)中测试它表明,无关紧要的移动总是在那里被省略.

这里发生了什么? 不能依赖被删除的初始化副本让我感到紧张.

注意:发布与调试版本的测试显示相同的结果.(我原本期望,因为复制省略与MSVC中的优化标志无关.)


要测试的完整源代码(另请参阅ideone链接):

#include "stdafx.h"
#include <iostream>
#include <map>

using namespace std;

struct LogMe {
    std::string …
Run Code Online (Sandbox Code Playgroud)

c++ copy-elision std-pair visual-c++-2012

5
推荐指数
1
解决办法
277
查看次数

RVO和复制省略只能在一个编译单元内工作吗?

它们是否跨不同的目标文件工作?它们是否跨不同的DLL工作?

我知道这取决于编译器.我很好奇是否有任何编译器和优化设置可以使这项工作.

c++ copy-elision rvo

5
推荐指数
1
解决办法
540
查看次数

移动构造函数与复制Elision.哪一个叫?

我这里有两段代码给你看.它们是两个类,每个类都提供一个Move Constructor和一个返回临时函数的函数.

  • 在第一种情况下,返回临时函数调用Move Constructor
  • 在第二种情况下,返回临时函数只是告诉编译器执行复制省略

我很困惑:在这两种情况下,我都定义了一个Move Constructor和一个返回临时的随机成员函数.但行为改变了,我的问题就是原因.

请注意,在以下示例中,运算符<<被重载以便打印列表(在第一种情况下)和双数据成员(在第二种情况下).


移动构造者得到了认可

template<typename T>
class GList
{
public:
    GList() : il{ nullptr } {}

    GList(const T& val) : il{ new Link<T>{ val,nullptr } }  {}

    GList(const GList<T>& copy) {}

    GList(GList<T>&& move)
    {
        std::cout << "[List] Move constructor called" << std::endl;

        // ... code ...
    }

    // HERE IS THE FUNCTION WHICH RETURNS A TEMPORARY!
    GList<T> Reverse()
    {
        GList<T> result;

        if (result.il == nullptr)
            return *this;

        ...
        ...
        ...

        return …
Run Code Online (Sandbox Code Playgroud)

move deep-copy move-constructor move-semantics copy-elision

5
推荐指数
1
解决办法
2081
查看次数

为什么即使允许copy-elision发生,代码也需要具有可访问的复制/移动构造函数?

Nicol Bolas 在SO的回答中写道:

在许多情况下允许复制省略.但是,即使允许,代码仍然必须能够工作,好像副本没有被删除.也就是说,必须有一个可访问的副本和/或移动构造函数.]

为什么有必要(在"保证副本省略"出现之前)代码维护复制/移动构造函数,即使允许复制省略发生?

为什么" 保证复制省略 "使程序员免于这些要求?

c++ language-lawyer copy-elision c++14

5
推荐指数
1
解决办法
183
查看次数

铿锵的"范围 - 循环 - 分析"诊断是什么?

背景:

请考虑以下示例:

#include <iostream>
#include <vector>

int main() {
    std::vector<bool> vectorBool{false, true};
    for(const auto &element : vectorBool) std::cout << std::boolalpha << element << ' ';
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

它会发出警告:

test.cpp:6:21: warning: loop variable 'element' is always a copy because the range of type 'std::vector<bool>' does not return a reference [-Wrange-loop-analysis]
    for(const auto &element : vectorBool) std::cout << std::boolalpha << element << ' ';
                    ^
test.cpp:6:9: note: use non-reference type 'std::_Bit_reference'
    for(const auto &element : vectorBool) std::cout << std::boolalpha …
Run Code Online (Sandbox Code Playgroud)

c++ clang compiler-warnings copy-elision clang++

5
推荐指数
1
解决办法
622
查看次数

隐式转换运算符与模板构造函数 - 谁应该优先考虑?

请考虑以下代码段:

template <typename>
struct dependent_false { static constexpr auto value = false; };

struct foo
{
    foo() { }

    template <typename T>
    foo(const T&) { static_assert(dependent_false<T>::value, ""); }
};

struct proxy
{
    operator foo() { return foo{};  }
};

int main()
{
    (void) foo{proxy{}};
}
Run Code Online (Sandbox Code Playgroud)

编译时-std=c++17:

  • clang++ (trunk)成功编译代码;

  • g++(trunk)无法编译代码 - 它实例化foo(const T&).

编译时-std=c++11,两个编译器都拒绝代码.C++ 17中新的prvalue实现规则可能会影响此处的行为.

godbolt.org上的实例


这里的正确行为是什么?

  • 标准是否保证foo::foo(const T&)将(或不会)实例化?

  • 标准是否保证隐式转换运算符优先于调用foo::foo(const T&),而不管它是否被实例化?

c++ conversion-operator language-lawyer copy-elision c++17

5
推荐指数
1
解决办法
184
查看次数

非平凡变量的常量正确性

(尽管与无关,但仍受此答案的启发)

我一直被告知(并且一直在告诉)const,即使对于寿命很短的变量,保持- 正确性也是有价值和良好的做法,例如:

const std::string a = "Hello world";
Run Code Online (Sandbox Code Playgroud)

代替

std::string a = "Hello world";
Run Code Online (Sandbox Code Playgroud)

这个:

  • 表达意图更清晰。
  • 确保变量是不可变的,因此将其传递给可能会更改它的某些函数将使编译器对您大吼大叫。
  • 由于编译器的优化,可能会提高性能。

尽管自从现代C ++引入复制省略功能以来,标准中已有一些子句允许编译器调用move构造函数而不是复制构造函数:

在以下复制初始化上下文中,可以使用移动操作代替复制操作:

(3.1) 如果return语句([stmt.return])中的表达式是一个(可能带有括号的)id表达式,该对象使用在最里面的封闭函数或lambda的主体或参数声明子句中声明的具有自动存储期限的对象进行命名-表达式,或

3.2) 如果throw-expression[[expr.throw])的操作数是非易失性自动对象(函数或catch子句参数除外)的名称,其范围不会超出最内层封闭的结尾try-block(如果有一个),

这是否意味着使用const带有非默认复制/移动构造函数的对象实际上会产生性能损失,而不是在遇到这种省略的情况时会增加性能吗?

c++ copy-elision

5
推荐指数
1
解决办法
135
查看次数