Omnifarious
Omnifarious

Reputation: 56068

constexpr if and the return value optimization

I have this code:

#include <string>

class A {
 public:
//    A(A const &) = delete;   // Code fails if this is uncommented.
    explicit A(int);
    explicit A(::std::string const &);

 private:
    ::std::string myname_;
    int foo_;
};

static constexpr bool which = false;

A test(::std::string const &s, int a)
{
    if constexpr (which) {
        A x{a};
        return x;
    } else {
        A y{s};
        return y;
    }
}

This code fails if A has a deleted copy constructor. But, given the rules about return type for functions with if constexpr in them, it seems like the compiler should be applying RVO here.

Is there a reason why it isn't, other than it being an overlooked case in the language specification?

Upvotes: 4

Views: 314

Answers (2)

bolov
bolov

Reputation: 75825

This has nothing to do with if constexpr

Simply this code is not allowed to compile:

class A {
 public:
    A(A const &) = delete;
    explicit A(int);
};

A test(int a)
{
    A x{a};
    return x; // <-- error call to a deleted constructor `A(A const &) = delete;`
}

The changes in C++17 you are thinking of have to do with temporary materialization and don't apply to NRVO because x is not a prvalue.

For instance this code was illegal pre C++17 and now it is allowed:

A test(int a)
{
    return A{a}; // legal since C++17
}

Upvotes: 3

songyuanyao
songyuanyao

Reputation: 172984

This is NRVO, which is non-mandatory copy elision:

(emphasis mine)

  • In a return statement, when the operand is the name of a non-volatile object with automatic storage duration, which isn't a function parameter or a catch clause parameter, and which is of the same class type (ignoring cv-qualification) as the function return type. This variant of copy elision is known as NRVO, "named return value optimization".

This is an optimization: even when it takes place and the copy/move (since C++11) constructor is not called, it still must be present and accessible (as if no optimization happened at all), otherwise the program is ill-formed:


BTW: Note that in your code, both the if part and else part of the constexpr if statement will be checked.

Outside a template, a discarded statement is fully checked. if constexpr is not a substitute for the #if preprocessing directive:

void f() {
    if constexpr(false) {
        int i = 0;
        int *p = i; // Error even though in discarded statement
    }
}

Upvotes: 1

Related Questions