I would like to ask you about the flow of processing when returning class variables in c++.
For example,
class Hoge {
// Class Implementation
};
Hoge func(){
Hoge fuga;
// Creating a fuga
return std::move(fuga);
}
int main() {
Hoge foo=func();
return 0;
}
If so, the flow of processing at the end of func is
Is that correct?
My question is
Thank you for your cooperation.
[Additional note]
The C++ version to be used is assumed to be C++14, but
If the behavior changes with C++17 or 20,
I would appreciate it if you could let me know including that.
Presentation code is a typical example of NRVO not being able to do it
#include<utility>
class Hoge {};
Hoge func1(){
Hogeh;
return;
}
Hoge func2(){
Hogeh;
return std::move(h);
}
For the code, g++/clang++ is
$g++-std=c++14-W-Wall-pedantic-O-g-cnrvo.cpp
nrvo.cpp: In function 'Hoge func2()':
nrvo.cpp: 9:21:warning:moving a local object in a return statement events copy elision [-Wpessimizing-move]
9 | return std::move(h);
| ~~~~~~~~~^~~
nrvo.cpp:9:21:note:remove'std::move'call
$ clang++-std=c++14-W-Wall-pedantic-O-g-cnrvo.cpp
nrvo.cpp: 9:12: warning: moving a local object in a return statement events copy elision [-Wpessimizing-move]
return std::move(h);
^
nrvo.cpp:9:12:note:remove std::move call here
return std::move(h);
^~~~~~~~~~ ~
1 warning generated.
$
In this example, it seems that it is better not to write std::move
because they complain as shown in .
$g++--version
g++ (GCC) 11.3.0
Copyright (C) 2021 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
$ clang++--version
clang version 8.0.1 (tags/RELEASE_801/final)
Target: x86_64-unknown-windows-cygnus
Thread model: posix
InstalledDir: /usr/bin
$ uname-a
CYGWIN_NT-10.0-19044 mypc 3.3.6-341.x86_64 2022-09-05 11:15 UTC x86_64 Cygwin
$
Incidentally, the warning was also displayed in -std=c++17
.
RVO/NRVO was implemented in most compilers since C++98 (although not required by language standards), so you should write without std::move
considering backward compatibility.
fuga
is the left side value, so I think I need to explicitly std::move
to move construct the return value of func
from fuga
. Is this correct?Wrong
The result of calling a function whose return type is not a reference is prvalue, regardless of whether the expression given in the return
expression is right or left.
prvalue
is considered a type of right-hand side value and moves occur if it appears as the initializer of foo
, but the rule prvalue
allows you to omit copying (must be omitted after C++17) applies, and it does not copy or move. It is considered to be the same as directly initialized
If you use std::move
for the expression given to return
, you will force a move on the temporary object generation. If you force a move where a copy is interpreted as an optional copy, the move is ensured.
In a simple interpretation, there are steps such as return
expression given to return
→ temporary object returned by variable (initiator of variable) → value passed to constructor, so it can be omitted with various conditions. In the example of this question, std::move
with the return
expression, but the return value of the function allows you to omit both copies and moves when initializing variables.
The flow of processing at the end of func is
Is that correct?
Both are partially correct.Strict behavior is as follows:
std::move
function is not utilized; that is, return fuga;
is preferred.At worst, implicit move construction is guaranteed.Since fuga is the left side value, I think it is necessary to explicitly std::move to construct the return value of func from fuga, but is this correct?
No.
If you specify the name (left side value) of the local variable in the return statement, the move construct is attempted first by rereading it as if it were the right side value.If this attempt results in a compilation error, the copy construct will be attempted again from the original left side value.
If the return value of the function initializes the variable, will the move construct be performed or will the constructor be omitted in NRVO etc.?
The answer is as above.The optimization here is not NRVO, but RVO.
It didn't come out when I asked the respondent, so I looked it up as far as I could easily (I can't even ask questions separately).
Leave a brief verification result
#include<iostream>
using namespace std;
structure Hoge {
Hoge() {cout<<"Hoge()"<<endl;}
Hoge(const Hoge&org) {cout<<"Hoge(const Hoge&)"<endl;}
Hoge&operator=(const Hoge&org) {cout<"operator=(const Hoge&)"<endl;return*this;}
~Hoge() {cout<"~Hoge()"<endl;}
# if (__cplusplusplus>=201103L)
Hoge(Hoge&org) {cout<<"Hoge(Hoge&&&endl;}
Hoge&operator=(Hoge&org) {cout<"operator=(Hoge&&)"<endl;return*this;}
#endif
};
Hoge func(){
Hoge fuga;
return fuga;
}
int main() {
Hoge foo1 = func();
Hoge foo2 = foo1;
foo2 = foo1;
# if (__cplusplusplus>=201103L)
Hoge foo3 = std::move(foo1);
foo3 = std::move(foo2);
#endif
return 0;
}
I tried to do this on the following site:
https://godbolt.org/
The following processing systems are covered:
clang x86-643.3, 3.4.1, 3.5.2, 3.6, 3.7, 3.8.1, 3.9.1, 4.0.1, 5.0.2, 6.0.1, 7.0.1, 7.1.0, 8.0.1, 10.0.1, 11.0.1, 12.0.1, 13.0.1, 14.0.0, 15.0.>
3.3, 3.4.1, 3.5.2, 3.6, 3.7, 3.8.1, 3.9.1, 4.0.1, 5.0.2, 6.0.1, 7.0.1, 7.0.1, 8.0.1, 10.0.1, 11.0.1, 12.0.1, 13.0.1, 14.0.0, 15.0.0
gcc x86-64 4.7.4, 4.8.5, 4.9.3, 5.5, 6.4, 7.5, 8.5, 9, 5, 10.4, 11.3, 12.2
msvc x64 19.33 (this is the only way to do it)
(If only the last version is different, it has not been implemented except for the largest version.)
Check Item 1
- Wall-std = c++98 and the output should be as follows:(msvc not implemented)
Hoge()
Hoge (const Hoge &)
operator=(const Hoge&)
~Hoge()
~Hoge()
Check Item 2
- Wall-std = c++ XX (where XX is the maximum value that can be compiled in 11, 14, 17, 2a, 20, 2b, 23) with the following output:
Hoge()
Hoge (const Hoge &)
operator=(const Hoge&)
Hoge (Hoge & & )
operator= (Hoge & & )
~Hoge()
~Hoge()
~Hoge()
(msvc is done at /std:c++20/Zc:__cplusplus/O2, remove /O2 and turn off optimization to get a copy.)
*Visual confirmation (I didn't see the assembler).It would be nice to have an online environment where you can do this kind of test.
Results
As for clang/gcc, I was able to confirm the omission of copy without optimization in all the versions listed.msvc only confirmed during optimization.
Additional
The output when msvc did not optimize was move constructor.
Hoge()
Hoge (Hoge & & )
~Hoge()
Hoge (const Hoge &)
operator=(const Hoge&)
Hoge (Hoge & & )
operator= (Hoge & & )
~Hoge()
~Hoge()
~Hoge()
540 Unable to install versioned in Google Colab
524 Uncaught (inpromise) Error on Electron: An object could not be cloned
679 When building Fast API+Uvicorn environment with PyInstaller, console=False results in an error
543 Understanding How to Configure Google API Key
546 Who developed the "avformat-59.dll" that comes with FFmpeg?
© 2024 OneMinuteCode. All rights reserved.