Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/147.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ c++;编译器优化a=b+;C_C++_Operator Overloading_Compiler Optimization - Fatal编程技术网

C++ c++;编译器优化a=b+;C

C++ c++;编译器优化a=b+;C,c++,operator-overloading,compiler-optimization,C++,Operator Overloading,Compiler Optimization,谁否决了我,介意解释一下原因吗?我认为这是一个合理的问题,所有的答案都非常有用。 理论上,当我执行MyClass a=b+c时,它应该首先调用const MyClass操作符+,返回const MyClass对象,然后调用赋值操作符来创建对象a 当返回一个对象并调用赋值操作符时,我似乎会复制两次内容。这是在编译器中优化的吗?如果是,如何进行?如果涉及到铸造,这似乎更棘手 让我们假设我们讨论的是G++,这是C++编译器的黄金标准。[编辑:好的,假设最常用] [编辑:]哇,我没想到使用const作为

谁否决了我,介意解释一下原因吗?我认为这是一个合理的问题,所有的答案都非常有用。

理论上,当我执行
MyClass a=b+c
时,它应该首先调用
const MyClass操作符+
,返回
const MyClass
对象,然后调用赋值操作符来创建对象a

当返回一个对象并调用赋值操作符时,我似乎会复制两次内容。这是在编译器中优化的吗?如果是,如何进行?如果涉及到铸造,这似乎更棘手

让我们假设我们讨论的是G++,这是C++编译器的黄金标准。[编辑:好的,假设最常用]


[编辑:]哇,我没想到使用const作为值的回报会受到批评。我认为在非内置类型按值返回时使用const是明智的?我记得在什么地方见过它

复制初始化不使用赋值运算符,它使用复制或移动构造函数。由于运算符愚蠢地返回一个
const
对象,因此移动是不可能的,因此它将使用复制构造函数


但是,从临时对象初始化对象是允许复制省略的情况之一,因此任何合适的编译器都应该这样做,直接将
a
初始化为返回值,而不是创建临时对象。

大多数编译器都会使用复制省略来优化此操作。通过调用
MyClass::operator+
创建的临时函数将直接构造到
a
中,而不是调用复制构造函数

还要注意,
MyClass a=…
不调用赋值运算符,而是调用复制构造函数。这称为复制初始化


查看有关复制省略的更多信息。

有一种称为复制省略的优化,如§12.8/31中的标准所述:

这种复制/移动操作的省略称为复制省略 在以下情况下允许(可合并为 消除多个副本):

  • 在具有类返回类型的函数中的
    return
    语句中,当表达式是 具有相同cv的非易失性自动对象(函数或catch子句参数除外)- 不合格类型作为函数返回类型,复制/移动操作可以通过构造 自动对象直接输入函数的返回值

  • 未绑定到引用(12.2)的临时类对象将被复制/移动到具有相同 cv不合格类型,复制/移动操作可以通过 将临时对象直接构造到 省略复制/移动

这样就行了

MyClass a = b + c;
operator+
返回的临时对象直接构造到
a
中,并且没有不必要的复制/移动,甚至在
operator+
中的
return
语句中也没有。示范:

struct MyClass
{
    int i;

    MyClass operator+( MyClass const& m ) {
        MyClass r = m.i + i;
        return r;
    }

    MyClass(int i) : i(i) {std::cout << "Ctor!\n";}
    // Move ctor not implicitly declared, see §12.8/9
    MyClass(MyClass const&) {std::cout << "Copy-Ctor!\n";}
    ~MyClass() {std::cout << "* Dtor!\n";}
};

int main() {
    MyClass c{7}, b{3},
            a = b + c;
}
struct MyClass
{
int i;
MyClass运算符+(MyClass常量和m){
MyClass r=m.i+i;
返回r;
}

MyClass(inti):i(i){std::cout为了更直接地了解您可以期望的内容,让我们从这样一个简单的类开始:

class Integer {
    int a;
    public:
    Integer(int a) : a(a) {}

    friend Integer operator+(Integer a, Integer b) {
        return Integer(a.a + b.a);
    }

    friend std::ostream &operator<<(std::ostream &os, Integer const &i) {
        return os << i.a;
    }
};
注意那里的
第20行
——它在下面变得很重要

现在,让我们编译它,看看编译器生成了什么代码。使用VC++我们可以得到:

[设置
main
elided]条目的正常“填充物”

; Line 19
    mov rcx, QWORD PTR [rdx+8]
    mov rdi, rdx
    call    atoi
    mov rcx, QWORD PTR [rdi+16]
    mov ebx, eax
    call    atoi
; Line 20
    lea edx, DWORD PTR [rax+rbx]
; Line 21
    call    ??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QEAAAEAV01@H@Z ; std::basic_ostream<char,std::char_traits<char> >::operator<<
它稍微重新排列了代码,但是ultimate做了几乎相同的事情——我们的
Integer
类的所有痕迹都消失了

让我们将其与完全不使用类的情况进行比较:

int main(int argc, char **argv) {
    int a = atoi(argv[1]);
    int b = atoi(argv[2]);
    int c = a + b;
    std::cout << c;
}
int main(int argc,char**argv){
int a=atoi(argv[1]);
intb=atoi(argv[2]);
INTC=a+b;

std::cout当然取决于您使用的编译器。返回
const MyClass
通常不是一个好主意,因为这会屏蔽移动语义。我说它还取决于
操作符+
本身以及它是否可以内联。没有赋值操作符,无论是理论上的还是其他方面的。黄金标准是,argua布莱,克朗。曾经有一个论点认为,对于相同类型的变量和初始值设定项表达式,它必须进行复制省略,但我不记得了。啊。你好,谢谢你的评论。我做了一些编辑。我没想到在按值返回时使用常量会受到批评。我认为在非内置类型的表达式中按值返回时使用常量是不可取的pes?我记得看到过somewhere@CodeNoob:有些人曾经建议这样做(这样像
a+b=c
这样的奇怪代码将无法编译,而不是给出奇怪的行为),但现在移动语义使它成为一个坏主意,因为你不能从一个常量对象移动。为什么投反对票?我说了什么不正确的话吗?
movq    8(%rbx), %rcx
call    atoi                    ; <--- get first item
movq    16(%rbx), %rcx
movl    %eax, %esi
call    atoi                    ; <--- get second item
movq    .refptr._ZSt4cout(%rip), %rcx
leal    (%rsi,%rax), %edx       ; <--- the addition
call    _ZNSolsEi               ; <--- print result
int main(int argc, char **argv) {
    int a = atoi(argv[1]);
    int b = atoi(argv[2]);
    int c = a + b;
    std::cout << c;
}
; Line 5
    mov rcx, QWORD PTR [rdx+8]
    mov rdi, rdx
    call    atoi
; Line 6
    mov rcx, QWORD PTR [rdi+16]
    mov ebx, eax
    call    atoi
; Line 7
    lea edx, DWORD PTR [rbx+rax]
; Line 8
    call    ??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QEAAAEAV01@H@Z ; std::basic_ostream<char,std::char_traits<char> >::operator<<