Каково влияние «явного» ключевого слова на оптимизацию возвращаемого значения (RVO)?

Следующий код работает отлично (показывая RVO):

struct A { 
  A (int) { cout << "A::A()\n"; }  // constructor
  A (const A&) { cout << "A::A(const A&)\n"; }  // copy constructor
};

A foo () { return A(0); }

int main () {
  A a = foo();
}

Выход:

A::A()  // --> which means copy constructor is not called

Если я отмечу конструктор копирования как explicit:

explicit A (const A&) { ... }

Затем компилятор выдает ошибку:

explicit.cpp: In function ‘A foo()’:
explicit.cpp:10:22: error: no matching function for call to ‘A::A(A)’
 A foo () { return A(0); }
                      ^
explicit.cpp:5:3: note: candidate: A::A(int)
   A (int) { cout << "A::A()\n"; }
   ^
explicit.cpp:5:3: note:   no known conversion for argument 1 from ‘A’ to ‘int’
explicit.cpp: In function ‘int main()’:
explicit.cpp:14:13: error: no matching function for call to ‘A::A(A)’
   A a = foo();
             ^
explicit.cpp:5:3: note: candidate: A::A(int)
   A (int) { cout << "A::A()\n"; }
   ^
explicit.cpp:5:3: note:   no known conversion for argument 1 from ‘A’ to ‘int’

Почему это происходит? Разве РВО не должен работать так, как есть?


person iammilind    schedule 02.08.2016    source источник
comment
К РВО это отношения не имеет.   -  person Konrad Rudolph    schedule 02.08.2016
comment
Похоже, это отвечает на него: stackoverflow.com/questions/29472565 /   -  person NathanOliver    schedule 02.08.2016
comment
@KonradRudolph, но без ключевого слова explicit происходит RVO, а не делается несколько копий A. Почему то же самое не должно продолжаться, когда мы помечаем конструктор как explicit? Q может не иметь прямого отношения к RVO, но я обнаружил некоторую косвенную связь между этими двумя вещами. @NathanOliver, спасибо, что указали. Этот Q показывает наблюдение. Однако этот вопрос о том, почему?   -  person iammilind    schedule 02.08.2016
comment
И на это был дан ответ в комментарии прямо под ответом. Я думаю, это не делает его обманом, но хех.   -  person NathanOliver    schedule 02.08.2016


Ответы (1)


RVO может исключить копию, но языковые правила требуют, чтобы копия (или перемещение) все еще была возможна:

[C++14: 12.8/31]: При соблюдении определенных критериев реализации разрешается пропускать конструкцию копирования/перемещения объекта класса, даже если конструктор, выбранный для операции копирования/перемещения, и/или деструктор объекта имеют побочные эффекты. [..]

[C++14: 12.8/32]: [..] [ Примечание. Это двухэтапное разрешение перегрузки должно выполняться независимо от того, произойдет ли удаление копии. Он определяет конструктор, который будет вызываться, если исключение не выполняется, и выбранный конструктор должен быть доступен, даже если вызов исключен. —конец примечания ]

Вы сделали копирование невозможным, добавив explicit, и перемещение невозможно, потому что ваш конструктор копирования блокирует создание неявно определенного конструктора перемещения.

Вместо этого вы можете разрешить перемещение, добавив свой собственный конструктор перемещения, возможно, по умолчанию:

A(A&&) = default;

Но это всего лишь другой способ соблюдения того же правила языка.

С++ 17 в любом случае несколько ослабит правило, добавив некоторые гарантии исключения копирования, на которые не распространяется это ограничение.

person Lightness Races in Orbit    schedule 02.08.2016
comment
(Игнорируя семантику перемещения для целей этого ответа, поскольку в коде вопроса явно ее нет.) - person Lightness Races in Orbit; 02.08.2016