Согласно cppreference
В константном выражении и константной инициализации исключение копирования никогда не выполняется.
Тем не менее, похоже, что на практике это не всегда так. В следующей программе функция f
возвращает объект res
типа A
, а также принимает на вход указатель p
на объект того же типа, и они могут быть равны &res == p
AFAIK только в случае оптимизации именованного возвращаемого значения (NRVO) A res = f( &res )
:
struct A {
bool nrvo = false;
};
constexpr A f( A * p = nullptr ) {
A res = p ? A{ .nrvo = &res == p } : f( &res );
return res;
}
// fails in MSVC
static_assert( !f().nrvo );
Поэтому я ожидал, что .nrvo=false
во время постоянной оценки, но это не так, по крайней мере, в компиляторе Visual Studio. Онлайн-демо: https://gcc.godbolt.org/z/xfKccM1sv
Является ли здесь неправильный компилятор или во время постоянных вычислений могут быть сделаны некоторые исключения копирования?
Да, согласно действующим правилам NRVO никогда не выполняется в контексте, требующем константного выражения, или во время инициализации константы. static_assert
должен добиться успеха.
Это было решено CWG 2278 в 2019 году. До этого отчета о дефектах правило было противоположным: предполагалось, что NRVO всегда применяется в таких контекстах, что оказалось нереализуемым.
Вышесказанное относится ко всем формам копирования, а не только к NRVO.
Однако «исключения копирования», которые были обязательными в C++17, такие как RVO из результата prvalue вызова функции для инициализации объекта, больше не являются исключениями копирования. Вместо этого это поведение стало реализовано как часть правил инициализации объекта и поэтому также применяется во время оценки константного выражения.
Список всех разрешенных исключений копирования можно найти в [class.copy.elision], как и нормативную формулировку утверждения cppreference:
Исключение копирования не допускается, если выражение оценивается в контексте, требующем константного выражения ([expr.const]) и при инициализации константы ([basic.start.static]).