考慮以下定義Person:
struct Person
{
Person() { std::cout << "construct, "; }
Person(const Person&) { std::cout << "copy\n"; }
Person(Person&&) { std::cout << "move\n"; }
};
以及 3 個不同的函式來創建一個Person:
Person create1()
{
std::cout << "create1: ";
Person p1{};
return p1;
}
Person create2()
{
std::cout << "create2: ";
if constexpr (true)
{
Person p1{};
return p1;
}
else
{
Person p2{};
return p2;
}
}
Person create3()
{
std::cout << "create3: ";
if constexpr (true)
{
return Person{};
}
else
{
return Person{};
}
}
最后,我呼叫 create 函式如下:
int main()
{
Person p1 = create1();
Person p2 = create2();
Person p3 = create3();
return 0;
}
輸出是:
create1: construct
create2: construct, move
create3: construct
困擾我的是create2. 如果在create1and 中create3,則沒有呼叫移動建構式,為什么在 中呼叫它create2?
我正在使用 GCC 12.0.0。
編輯:Clang 13.0.0 不呼叫移動建構式。
uj5u.com熱心網友回復:
我認為它是常量運算式中 NRVO 的一個實體,cppreference 說:
在常量運算式和常量初始化中,保證回傳值優化(RVO),但禁止命名回傳值優化(NRVO):
這與CWG 2278相關。
編輯
我重新考慮了 :-) 它是從 呼叫的main(),所以它不能是常量初始化。通過實際的常量初始化,gcc 和 clang 都不會做 NRVO。在這個例子中(見 Godbolt):
struct Person {
bool was_moved;
constexpr Person() : was_moved{false} { }
constexpr Person(const Person&) : was_moved{false} { }
constexpr Person(Person&&) : was_moved{true} { }
};
constexpr Person create() {
if (true) {
Person p1;
return p1;
}
else {
Person p2;
return p2;
}
}
constexpr Person p = create();
int main() {
return p.was_moved;
}
p.was_moved 是 1。
至于為什么 gcc 不在 中執行 NRVO create2(),根據之前的 cppreference 鏈接,一個答案可能是它不是強制性的。另一個可能是 NRVO 的 gcc 實作非常“脆弱”。例如,它在只涉及一個命名變數時起作用:
Person create4() {
Person p{};
if (true) {
return p;
}
else {
return p;
}
}
但是當這整個事情被放在一個子范圍內時停止作業:
Person create5() {
{
Person p{};
if (true) {
return p;
}
else {
return p;
}
}
}
(見天箭)
關于這兩個問題,2012 年有 gcc 錯誤:錯誤 53637 - NRVO 不適用于涉及兩個不同變數的情況。
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/368037.html
上一篇:Boost轉換為向量誤差
