c++movenrvo

Why does one call of this function use NRVO while another call of the same function does not?


vector is a struct with two public members X and Y; a parameterized constructor that prints integer constructor called; a copy constructor that prints copy constructor called; and a move constructor that prints move constructor called <== This line is different\n.

The function makeVectorx5y5IfNotZero() returns the vector {5, 5} if arg is 0. otherwise it returns {2, 2}.

The main function constructs vector A using makeVectorx5y5IfNotZero(1) and constructs vector B using makeVectorx5y5IfNotZero(0).

Here is the code:

#include <iostream>

struct vector{
    int X, Y;
    vector(int X, int Y) : X(X), Y(Y){
        printf("integer constructor called\n");
    }
    vector(const vector& other): X(other.X), Y(other.Y){
        printf("copy constructor called\n");
    }
    vector(vector&& other): X(other.X), Y(other.Y){
        printf("move constructor called      <== This line is different\n\n");
    }
};

vector makeVectorx5y5IfNotZero(bool Arg){
    if(Arg){
        vector A(0, 0);
        A.X = 5;
        A.Y = 5;
        return A;
    }
    else {
        vector B(1,1);
        B.X = 2;
        B.Y = 2;
        return B;
    }
}

int main(){
    printf("Making Vector\n");
    vector A = makeVectorx5y5IfNotZero(1);
    printf("Made Vector\n");
    printf("Making Another Vector\n");
    vector B = makeVectorx5y5IfNotZero(0);
    printf("Made Vector\n");
}

The output of this code is:

Making Vector
integer constructor called
Made Vector
Making Another Vector
integer constructor called
move constructor called      <== This line is different
Made Vector

However, I expected the following output, because of NRVO:

Making Vector
integer constructor called
Made Vector
Making Another Vector
integer constructor called
Made Vector

Why is the local variable B of makeVectorx5y5IfNotZero() being moved into the local variable B of main(), rather than just constructing a single variable at the same memory address?

Compiler version (output of g++ --version):

g++ (Rev3, Built by MSYS2 project) 14.1.0
Copyright (C) 2024 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

Solution

  • Quoting @PepijnKramer in the comments

    It is a gcc limitation (it doesn't optimize the branch)

    This discussed more in detail in this video C++ Weekly - Ep 456 - RVO + Trivial Types = Faster Code