返回默认构造的对象会阻止 NRVO 吗?
Does returning a default constructed object prevent NRVO?
假设我有这样的功能:
using std::vector;
vector<int> build_vector(int n)
{
if (some_condition(n)) return {};
vector<int> out;
for(int x : something())
{
out.push_back(x);
}
return out;
}
函数开头的return {}
是否可以防止NRVO?我很好奇,因为这似乎等同于以下内容:
using std::vector;
vector<int> nrvo_friendly_build_vector(int n)
{
vector<int> out;
if (some_condition(n)) return out;
for(int x : something())
{
out.push_back(x);
}
return out;
}
但我不清楚在第一种情况下是否允许编译器执行 NRVO。
来自https://en.cppreference.com/w/cpp/language/copy_elision
Under the following circumstances, the compilers are permitted, but not required to omit the copy and move (since C++11) construction of class objects even if the copy/move (since C++11) constructor and the destructor have observable side-effects. The objects are constructed directly into the storage where they would otherwise be copied/moved to. 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:
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".
...
早期 return 没有限制,因此两个版本都是 NRVO 的候选版本。
As requested by the OP, here is an adapted version of
我实际上也在想同样的事情(特别是因为复制省略不是"required"标准),所以我在在线编译器中通过替换快速测试它std::vector
通过 Widget 结构:
struct Widget
{
int val = 0;
Widget() { printf("default ctor\n"); }
Widget(const Widget&) { printf("copy ctor\n"); }
Widget(Widget&&) { printf("move ctor\n"); }
Widget& operator=(const Widget&) { printf("copy assign\n"); return *this; }
Widget& operator=(Widget&&) { printf("move assign\n"); return *this; }
~Widget() { printf("dtor\n"); }
void method(int)
{
printf("method\n");
}
};
V1 使用 build_vector()
:http://coliru.stacked-crooked.com/a/5e55efe46bfe32f5
#include <cstdio>
#include <array>
#include <cstdlib>
using std::array;
struct Widget
{
int val = 0;
Widget() { printf("default ctor\n"); }
Widget(const Widget&) { printf("copy ctor\n"); }
Widget(Widget&&) { printf("move ctor\n"); }
Widget& operator=(const Widget&) { printf("copy assign\n"); return *this; }
Widget& operator=(Widget&&) { printf("move assign\n"); return *this; }
~Widget() { printf("dtor\n"); }
void method(int)
{
printf("method\n");
}
};
bool some_condition(int x)
{
return (x % 2) == 0;
}
array<int, 3> something()
{
return {{1,2,3}};
}
Widget build_vector(int n)
{
if (some_condition(n)) return {};
Widget out;
for(int x : something())
{
out.method(x);
}
return out;
}
int main(int argc, char* argv[])
{
if (argc != 2)
{
return -1;
}
const int x = atoi(argv[1]);
printf("call build_vector\n");
Widget w = build_vector(x);
printf("end of call\n");
return w.val;
}
V1的输出
call build_vector
default ctor
method
method
method
move ctor
dtor
end of call
dtor
V2 使用 nrvo_friendly_build_vector()
:http://coliru.stacked-crooked.com/a/51b036c66e993d62
#include <cstdio>
#include <array>
#include <cstdlib>
using std::array;
struct Widget
{
int val = 0;
Widget() { printf("default ctor\n"); }
Widget(const Widget&) { printf("copy ctor\n"); }
Widget(Widget&&) { printf("move ctor\n"); }
Widget& operator=(const Widget&) { printf("copy assign\n"); return *this; }
Widget& operator=(Widget&&) { printf("move assign\n"); return *this; }
~Widget() { printf("dtor\n"); }
void method(int)
{
printf("method\n");
}
};
bool some_condition(int x)
{
return (x % 2) == 0;
}
array<int, 3> something()
{
return {{1,2,3}};
}
Widget nrvo_friendly_build_vector(int n)
{
Widget out;
if (some_condition(n)) return out;
for(int x : something())
{
out.method(x);
}
return out;
}
int main(int argc, char* argv[])
{
if (argc != 2)
{
return -1;
}
const int x = atoi(argv[1]);
printf("call nrvo_friendly_build_vector\n");
Widget w = nrvo_friendly_build_vector(x);
printf("end of call\n");
return w.val;
}
V2的输出
call nrvo_friendly_build_vector
default ctor
method
method
method
end of call
dtor
如您所见,在这种特殊情况下 (some_condition 看不到构造结构的副作用),如果 some_condition()
为假 (至少在 clang 和 gcc 中,使用 -std=c++11
和 -O2
,在 Coliru 中)
此外,作为 you have noticed,同样的行为似乎也发生在 -O3
。
HTH
ps:在学习复制省略时,您可能会发现 Abseil's ToW #11 很有趣;)
假设我有这样的功能:
using std::vector;
vector<int> build_vector(int n)
{
if (some_condition(n)) return {};
vector<int> out;
for(int x : something())
{
out.push_back(x);
}
return out;
}
函数开头的return {}
是否可以防止NRVO?我很好奇,因为这似乎等同于以下内容:
using std::vector;
vector<int> nrvo_friendly_build_vector(int n)
{
vector<int> out;
if (some_condition(n)) return out;
for(int x : something())
{
out.push_back(x);
}
return out;
}
但我不清楚在第一种情况下是否允许编译器执行 NRVO。
来自https://en.cppreference.com/w/cpp/language/copy_elision
Under the following circumstances, the compilers are permitted, but not required to omit the copy and move (since C++11) construction of class objects even if the copy/move (since C++11) constructor and the destructor have observable side-effects. The objects are constructed directly into the storage where they would otherwise be copied/moved to. 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:
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".
...
早期 return 没有限制,因此两个版本都是 NRVO 的候选版本。
As requested by the OP, here is an adapted version of
我实际上也在想同样的事情(特别是因为复制省略不是"required"标准),所以我在在线编译器中通过替换快速测试它std::vector
通过 Widget 结构:
struct Widget
{
int val = 0;
Widget() { printf("default ctor\n"); }
Widget(const Widget&) { printf("copy ctor\n"); }
Widget(Widget&&) { printf("move ctor\n"); }
Widget& operator=(const Widget&) { printf("copy assign\n"); return *this; }
Widget& operator=(Widget&&) { printf("move assign\n"); return *this; }
~Widget() { printf("dtor\n"); }
void method(int)
{
printf("method\n");
}
};
V1 使用 build_vector()
:http://coliru.stacked-crooked.com/a/5e55efe46bfe32f5
#include <cstdio>
#include <array>
#include <cstdlib>
using std::array;
struct Widget
{
int val = 0;
Widget() { printf("default ctor\n"); }
Widget(const Widget&) { printf("copy ctor\n"); }
Widget(Widget&&) { printf("move ctor\n"); }
Widget& operator=(const Widget&) { printf("copy assign\n"); return *this; }
Widget& operator=(Widget&&) { printf("move assign\n"); return *this; }
~Widget() { printf("dtor\n"); }
void method(int)
{
printf("method\n");
}
};
bool some_condition(int x)
{
return (x % 2) == 0;
}
array<int, 3> something()
{
return {{1,2,3}};
}
Widget build_vector(int n)
{
if (some_condition(n)) return {};
Widget out;
for(int x : something())
{
out.method(x);
}
return out;
}
int main(int argc, char* argv[])
{
if (argc != 2)
{
return -1;
}
const int x = atoi(argv[1]);
printf("call build_vector\n");
Widget w = build_vector(x);
printf("end of call\n");
return w.val;
}
V1的输出
call build_vector
default ctor
method
method
method
move ctor
dtor
end of call
dtor
V2 使用 nrvo_friendly_build_vector()
:http://coliru.stacked-crooked.com/a/51b036c66e993d62
#include <cstdio>
#include <array>
#include <cstdlib>
using std::array;
struct Widget
{
int val = 0;
Widget() { printf("default ctor\n"); }
Widget(const Widget&) { printf("copy ctor\n"); }
Widget(Widget&&) { printf("move ctor\n"); }
Widget& operator=(const Widget&) { printf("copy assign\n"); return *this; }
Widget& operator=(Widget&&) { printf("move assign\n"); return *this; }
~Widget() { printf("dtor\n"); }
void method(int)
{
printf("method\n");
}
};
bool some_condition(int x)
{
return (x % 2) == 0;
}
array<int, 3> something()
{
return {{1,2,3}};
}
Widget nrvo_friendly_build_vector(int n)
{
Widget out;
if (some_condition(n)) return out;
for(int x : something())
{
out.method(x);
}
return out;
}
int main(int argc, char* argv[])
{
if (argc != 2)
{
return -1;
}
const int x = atoi(argv[1]);
printf("call nrvo_friendly_build_vector\n");
Widget w = nrvo_friendly_build_vector(x);
printf("end of call\n");
return w.val;
}
V2的输出
call nrvo_friendly_build_vector
default ctor
method
method
method
end of call
dtor
如您所见,在这种特殊情况下 (some_condition 看不到构造结构的副作用),如果 some_condition()
为假 (至少在 clang 和 gcc 中,使用 -std=c++11
和 -O2
,在 Coliru 中)
此外,作为 you have noticed,同样的行为似乎也发生在 -O3
。
HTH
ps:在学习复制省略时,您可能会发现 Abseil's ToW #11 很有趣;)