Move semantics in derived-to-base class conversionsWhat is move semantics?What is std::move(), and when should it be used?Move constructor on derived objectC++11 rvalues and move semantics confusion (return statement)Classes, Rvalues and Rvalue ReferencesWill member subobjects of local variables be moved too if returned from a function?lvalue to rvalue implicit conversionUnderstanding the code for std::move()Why are move semantics necessary to elide temporary copies?C++ move semantics - when to return an rvalue reference
How would someone destroy a black hole that’s at the centre of a planet?
Absconding a company after 1st day of joining
Can I activate an iPhone without an Apple ID?
How did John Lennon tune his guitar
Do native speakers use ZVE or CPU?
Why hasn't the U.S. government paid war reparations to any country it attacked?
Why does Hellboy file down his horns?
What is the English equivalent of 干物女 (dried fish woman)?
Verifying Hamiltonian Cycle solution in O(n^2), n is the length of the encoding of G
As a DM, how to avoid unconscious metagaming when dealing with a high AC character?
What impact would a dragon the size of Asia have on the environment?
Why limit to revolvers?
How to use "regular expression" to separate specific strings in Oracle
Hot object in a vacuum
What are some symbols representing peasants/oppressed persons fighting back?
Equivalent definitions of total angular momentum
How to fit a linear model in the Bayesian way in Mathematica?
Meaning of slash chord without anything left of the slash
Add region constraint to Graphics
Why do they not say "The Baby"
Can I capture stereo IQ signals from WebSDR?
Integral clarification
Deep Learning based time series forecasting
Redox reactions redefined
Move semantics in derived-to-base class conversions
What is move semantics?What is std::move(), and when should it be used?Move constructor on derived objectC++11 rvalues and move semantics confusion (return statement)Classes, Rvalues and Rvalue ReferencesWill member subobjects of local variables be moved too if returned from a function?lvalue to rvalue implicit conversionUnderstanding the code for std::move()Why are move semantics necessary to elide temporary copies?C++ move semantics - when to return an rvalue reference
.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty,.everyoneloves__bot-mid-leaderboard:empty margin-bottom:0;
Consider the following class Buffer, which contains an std::vector object:
#include <vector>
#include <cstddef>
class Buffer
std::vector<std::byte> buf_;
protected:
Buffer(std::byte val): buf_(1024, val)
;
Now, consider the function make_zeroed_buffer() below. The class BufferBuilder is a local class that publicly derives from Buffer. Its purpose is to create Buffer objects.
Buffer make_zeroed_buffer()
struct BufferBuilder: Buffer
BufferBuilder(): Buffer(std::byte0)
;
BufferBuilder buffer;
// ...
return buffer;
If no copy elision takes place, is the buffer object above guaranteed to be moved from?
My reasoning is the following:
- The expression
bufferin thereturnstatement is an lvalue. Since it is a local object that is not going to be used anymore, the compiler casts it into an rvalue. - The
bufferobject is of typeBufferBuilder.Bufferis a public base class ofBufferBuilder, so thisBufferBuilderobject is implicitly converted into aBufferobject. - This conversion, in turn, implies an implicit reference-to-derived to a reference-to-base conversion (i.e., a reference to
BufferBuilderto a reference toBuffer). That reference toBufferBuilderis an rvalue reference (see 1.), which turns into an rvalue reference toBuffer. - The rvalue reference to
BuffermatchesBuffer's move constructor, which is used to construct theBufferobject thatmake_zeroed_buffer()returns by value. As a result, the return value is constructed by moving from theBufferpart of the objectbuffer.
c++ c++17 move-semantics derived-class object-slicing
|
show 5 more comments
Consider the following class Buffer, which contains an std::vector object:
#include <vector>
#include <cstddef>
class Buffer
std::vector<std::byte> buf_;
protected:
Buffer(std::byte val): buf_(1024, val)
;
Now, consider the function make_zeroed_buffer() below. The class BufferBuilder is a local class that publicly derives from Buffer. Its purpose is to create Buffer objects.
Buffer make_zeroed_buffer()
struct BufferBuilder: Buffer
BufferBuilder(): Buffer(std::byte0)
;
BufferBuilder buffer;
// ...
return buffer;
If no copy elision takes place, is the buffer object above guaranteed to be moved from?
My reasoning is the following:
- The expression
bufferin thereturnstatement is an lvalue. Since it is a local object that is not going to be used anymore, the compiler casts it into an rvalue. - The
bufferobject is of typeBufferBuilder.Bufferis a public base class ofBufferBuilder, so thisBufferBuilderobject is implicitly converted into aBufferobject. - This conversion, in turn, implies an implicit reference-to-derived to a reference-to-base conversion (i.e., a reference to
BufferBuilderto a reference toBuffer). That reference toBufferBuilderis an rvalue reference (see 1.), which turns into an rvalue reference toBuffer. - The rvalue reference to
BuffermatchesBuffer's move constructor, which is used to construct theBufferobject thatmake_zeroed_buffer()returns by value. As a result, the return value is constructed by moving from theBufferpart of the objectbuffer.
c++ c++17 move-semantics derived-class object-slicing
2
If no copy elision takes place What makes you think that this condition will ever be satisfied? From my recollection, when you explicitlyreturn std::move(buffer);the compiler (clang) will issue a warning about surpressing copy elision.
– Walter
8 hours ago
@Walter What makes you think then that copy elision will always be satisfied?
– El Profesor
8 hours ago
If I remember well, in this very particular case you have to usereturn std::move(buffer)
– Biagio Festa
8 hours ago
2
No copy elision. The next statement is violated: In a return statement, when the operand is a prvalue of the same class type (ignoring cv-qualification) as the function return type
– S.M.
7 hours ago
1
The buffer object is of type BufferBuilder. Buffer is a public base class of BufferBuilder, so this BufferBuilder object is implicitly converted into a Buffer object. It is not true. The BufferBuilder object is copied implicitly to a Builder object. You confused with pointers to a derived class.
– S.M.
7 hours ago
|
show 5 more comments
Consider the following class Buffer, which contains an std::vector object:
#include <vector>
#include <cstddef>
class Buffer
std::vector<std::byte> buf_;
protected:
Buffer(std::byte val): buf_(1024, val)
;
Now, consider the function make_zeroed_buffer() below. The class BufferBuilder is a local class that publicly derives from Buffer. Its purpose is to create Buffer objects.
Buffer make_zeroed_buffer()
struct BufferBuilder: Buffer
BufferBuilder(): Buffer(std::byte0)
;
BufferBuilder buffer;
// ...
return buffer;
If no copy elision takes place, is the buffer object above guaranteed to be moved from?
My reasoning is the following:
- The expression
bufferin thereturnstatement is an lvalue. Since it is a local object that is not going to be used anymore, the compiler casts it into an rvalue. - The
bufferobject is of typeBufferBuilder.Bufferis a public base class ofBufferBuilder, so thisBufferBuilderobject is implicitly converted into aBufferobject. - This conversion, in turn, implies an implicit reference-to-derived to a reference-to-base conversion (i.e., a reference to
BufferBuilderto a reference toBuffer). That reference toBufferBuilderis an rvalue reference (see 1.), which turns into an rvalue reference toBuffer. - The rvalue reference to
BuffermatchesBuffer's move constructor, which is used to construct theBufferobject thatmake_zeroed_buffer()returns by value. As a result, the return value is constructed by moving from theBufferpart of the objectbuffer.
c++ c++17 move-semantics derived-class object-slicing
Consider the following class Buffer, which contains an std::vector object:
#include <vector>
#include <cstddef>
class Buffer
std::vector<std::byte> buf_;
protected:
Buffer(std::byte val): buf_(1024, val)
;
Now, consider the function make_zeroed_buffer() below. The class BufferBuilder is a local class that publicly derives from Buffer. Its purpose is to create Buffer objects.
Buffer make_zeroed_buffer()
struct BufferBuilder: Buffer
BufferBuilder(): Buffer(std::byte0)
;
BufferBuilder buffer;
// ...
return buffer;
If no copy elision takes place, is the buffer object above guaranteed to be moved from?
My reasoning is the following:
- The expression
bufferin thereturnstatement is an lvalue. Since it is a local object that is not going to be used anymore, the compiler casts it into an rvalue. - The
bufferobject is of typeBufferBuilder.Bufferis a public base class ofBufferBuilder, so thisBufferBuilderobject is implicitly converted into aBufferobject. - This conversion, in turn, implies an implicit reference-to-derived to a reference-to-base conversion (i.e., a reference to
BufferBuilderto a reference toBuffer). That reference toBufferBuilderis an rvalue reference (see 1.), which turns into an rvalue reference toBuffer. - The rvalue reference to
BuffermatchesBuffer's move constructor, which is used to construct theBufferobject thatmake_zeroed_buffer()returns by value. As a result, the return value is constructed by moving from theBufferpart of the objectbuffer.
c++ c++17 move-semantics derived-class object-slicing
c++ c++17 move-semantics derived-class object-slicing
edited 6 hours ago
Christopher Oezbek
11.4k3 gold badges36 silver badges60 bronze badges
11.4k3 gold badges36 silver badges60 bronze badges
asked 8 hours ago
El ProfesorEl Profesor
11.3k3 gold badges27 silver badges42 bronze badges
11.3k3 gold badges27 silver badges42 bronze badges
2
If no copy elision takes place What makes you think that this condition will ever be satisfied? From my recollection, when you explicitlyreturn std::move(buffer);the compiler (clang) will issue a warning about surpressing copy elision.
– Walter
8 hours ago
@Walter What makes you think then that copy elision will always be satisfied?
– El Profesor
8 hours ago
If I remember well, in this very particular case you have to usereturn std::move(buffer)
– Biagio Festa
8 hours ago
2
No copy elision. The next statement is violated: In a return statement, when the operand is a prvalue of the same class type (ignoring cv-qualification) as the function return type
– S.M.
7 hours ago
1
The buffer object is of type BufferBuilder. Buffer is a public base class of BufferBuilder, so this BufferBuilder object is implicitly converted into a Buffer object. It is not true. The BufferBuilder object is copied implicitly to a Builder object. You confused with pointers to a derived class.
– S.M.
7 hours ago
|
show 5 more comments
2
If no copy elision takes place What makes you think that this condition will ever be satisfied? From my recollection, when you explicitlyreturn std::move(buffer);the compiler (clang) will issue a warning about surpressing copy elision.
– Walter
8 hours ago
@Walter What makes you think then that copy elision will always be satisfied?
– El Profesor
8 hours ago
If I remember well, in this very particular case you have to usereturn std::move(buffer)
– Biagio Festa
8 hours ago
2
No copy elision. The next statement is violated: In a return statement, when the operand is a prvalue of the same class type (ignoring cv-qualification) as the function return type
– S.M.
7 hours ago
1
The buffer object is of type BufferBuilder. Buffer is a public base class of BufferBuilder, so this BufferBuilder object is implicitly converted into a Buffer object. It is not true. The BufferBuilder object is copied implicitly to a Builder object. You confused with pointers to a derived class.
– S.M.
7 hours ago
2
2
If no copy elision takes place What makes you think that this condition will ever be satisfied? From my recollection, when you explicitly
return std::move(buffer); the compiler (clang) will issue a warning about surpressing copy elision.– Walter
8 hours ago
If no copy elision takes place What makes you think that this condition will ever be satisfied? From my recollection, when you explicitly
return std::move(buffer); the compiler (clang) will issue a warning about surpressing copy elision.– Walter
8 hours ago
@Walter What makes you think then that copy elision will always be satisfied?
– El Profesor
8 hours ago
@Walter What makes you think then that copy elision will always be satisfied?
– El Profesor
8 hours ago
If I remember well, in this very particular case you have to use
return std::move(buffer)– Biagio Festa
8 hours ago
If I remember well, in this very particular case you have to use
return std::move(buffer)– Biagio Festa
8 hours ago
2
2
No copy elision. The next statement is violated: In a return statement, when the operand is a prvalue of the same class type (ignoring cv-qualification) as the function return type
– S.M.
7 hours ago
No copy elision. The next statement is violated: In a return statement, when the operand is a prvalue of the same class type (ignoring cv-qualification) as the function return type
– S.M.
7 hours ago
1
1
The buffer object is of type BufferBuilder. Buffer is a public base class of BufferBuilder, so this BufferBuilder object is implicitly converted into a Buffer object. It is not true. The BufferBuilder object is copied implicitly to a Builder object. You confused with pointers to a derived class.
– S.M.
7 hours ago
The buffer object is of type BufferBuilder. Buffer is a public base class of BufferBuilder, so this BufferBuilder object is implicitly converted into a Buffer object. It is not true. The BufferBuilder object is copied implicitly to a Builder object. You confused with pointers to a derived class.
– S.M.
7 hours ago
|
show 5 more comments
2 Answers
2
active
oldest
votes
RVO Optimisation
If no copy elision takes place [...]
Actually, copy elision will not take place (without if).
From C++ standard class.copy.elision#1:
is permitted in the following circumstances [...]:
-- in a return statement in a function with a class return type, when the expression is the name of a non-volatile automatic object ([...]) with the same type (ignoring cv-qualification) as the function return type [...]
Technically, when you return a derived class and a slicing operation takes place, the RVO cannot be applied.
Technically RVO works constructing the local object on the returning space on the stack frame.
|--------------|
| local vars |
|--------------|
| return addr |
|--------------|
| return obj |
|--------------|
Generally, a derived class can have a different memory layout than its parent (different size, alignments, ...). So there is no guarantee the local object (derived) can be constructed in the place reserved for the returned object (parent).
Implicit move
Now, what about implicit move?
is the buffer object above guaranteed to be moved from???
In short: no. On the contrary, it is guaranteed the object will be copied!
In this particular case implicit move will not be performed because of slicing.
In short, this happens because the overload resolution fails.
It tries to match against the move-constructor (Buffer::Buffer(Buffer&&)) whereas you have a BufferBuild object). So it fallbacks on the copy constructor.
From C++ standard class.copy.elision#3:
[...] if the type of the first parameter of the selected constructor or the return_value overload is not an rvalue reference to the object's type (possibly cv-qualified), overload resolution is performed again, considering the object as an lvalue.
Therefore, since the first overload resolution fails (as I have said above), the expression will be treated as an lvalue (and not an rvalue), inhibiting the move.
An interesting talk by Arthur O'Dwyer specifically refers to this case. Youtube Video.
Additional Note
On clang, you can pass the flag -Wmove in order to detect this kind of problems.
Indeed for your code:
local variable 'buffer' will be copied despite being returned by name [-Wreturn-std-move]
return buffer;
^~~~~~
<source>:20:11: note: call 'std::move' explicitly to avoid copying
return buffer;
clang directly suggests you to use std::move on the return expression.
1
In standardese, the implicit move is disallowed because "the type of the first parameter of the selected constructor (Buffer&&) is not an rvalue reference to the object's type (BufferBuilder&&)" eel.is/c++draft/class.copy.elision#3.sentence-2
– Oktalist
7 hours ago
Yeah, that what I've written (with other words). But thank you, maybe I can rephrase better and add a reference to the standard.
– Biagio Festa
7 hours ago
You phrased it fine, I just wanted to show the "proof".
– Oktalist
7 hours ago
If my reading of the spec is correct, this modification of the local class should make the compiler move it:struct BufferBuilder BufferBuilder():buffer(std::byte0) Buffer buffer; operator Buffer() && return (Buffer&&)buffer; ;Because there is no "selected constructor" anymore. (EDIT: needs a public constructor in Buffer :/)
– Johannes Schaub - litb
5 hours ago
Clang disagrees with my reading. GCC agrees. Clang says: "<source>:16:7: note: candidate function not viable: no known conversion from 'BufferBuilder' to 'BufferBuilder' for object argument operator Buffer() && return (Buffer&&)buffer; ". I think that this is a bug in Clang.
– Johannes Schaub - litb
4 hours ago
add a comment |
The object buffer from make_zeroed_buffer() will destroyed after will be made its copy with the help of Buffers' copy constructor for return value.
New contributor
zooorkin is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.
add a comment |
Your Answer
StackExchange.ifUsing("editor", function ()
StackExchange.using("externalEditor", function ()
StackExchange.using("snippets", function ()
StackExchange.snippets.init();
);
);
, "code-snippets");
StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "1"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);
else
createEditor();
);
function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader:
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
,
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);
);
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f57028409%2fmove-semantics-in-derived-to-base-class-conversions%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
2 Answers
2
active
oldest
votes
2 Answers
2
active
oldest
votes
active
oldest
votes
active
oldest
votes
RVO Optimisation
If no copy elision takes place [...]
Actually, copy elision will not take place (without if).
From C++ standard class.copy.elision#1:
is permitted in the following circumstances [...]:
-- in a return statement in a function with a class return type, when the expression is the name of a non-volatile automatic object ([...]) with the same type (ignoring cv-qualification) as the function return type [...]
Technically, when you return a derived class and a slicing operation takes place, the RVO cannot be applied.
Technically RVO works constructing the local object on the returning space on the stack frame.
|--------------|
| local vars |
|--------------|
| return addr |
|--------------|
| return obj |
|--------------|
Generally, a derived class can have a different memory layout than its parent (different size, alignments, ...). So there is no guarantee the local object (derived) can be constructed in the place reserved for the returned object (parent).
Implicit move
Now, what about implicit move?
is the buffer object above guaranteed to be moved from???
In short: no. On the contrary, it is guaranteed the object will be copied!
In this particular case implicit move will not be performed because of slicing.
In short, this happens because the overload resolution fails.
It tries to match against the move-constructor (Buffer::Buffer(Buffer&&)) whereas you have a BufferBuild object). So it fallbacks on the copy constructor.
From C++ standard class.copy.elision#3:
[...] if the type of the first parameter of the selected constructor or the return_value overload is not an rvalue reference to the object's type (possibly cv-qualified), overload resolution is performed again, considering the object as an lvalue.
Therefore, since the first overload resolution fails (as I have said above), the expression will be treated as an lvalue (and not an rvalue), inhibiting the move.
An interesting talk by Arthur O'Dwyer specifically refers to this case. Youtube Video.
Additional Note
On clang, you can pass the flag -Wmove in order to detect this kind of problems.
Indeed for your code:
local variable 'buffer' will be copied despite being returned by name [-Wreturn-std-move]
return buffer;
^~~~~~
<source>:20:11: note: call 'std::move' explicitly to avoid copying
return buffer;
clang directly suggests you to use std::move on the return expression.
1
In standardese, the implicit move is disallowed because "the type of the first parameter of the selected constructor (Buffer&&) is not an rvalue reference to the object's type (BufferBuilder&&)" eel.is/c++draft/class.copy.elision#3.sentence-2
– Oktalist
7 hours ago
Yeah, that what I've written (with other words). But thank you, maybe I can rephrase better and add a reference to the standard.
– Biagio Festa
7 hours ago
You phrased it fine, I just wanted to show the "proof".
– Oktalist
7 hours ago
If my reading of the spec is correct, this modification of the local class should make the compiler move it:struct BufferBuilder BufferBuilder():buffer(std::byte0) Buffer buffer; operator Buffer() && return (Buffer&&)buffer; ;Because there is no "selected constructor" anymore. (EDIT: needs a public constructor in Buffer :/)
– Johannes Schaub - litb
5 hours ago
Clang disagrees with my reading. GCC agrees. Clang says: "<source>:16:7: note: candidate function not viable: no known conversion from 'BufferBuilder' to 'BufferBuilder' for object argument operator Buffer() && return (Buffer&&)buffer; ". I think that this is a bug in Clang.
– Johannes Schaub - litb
4 hours ago
add a comment |
RVO Optimisation
If no copy elision takes place [...]
Actually, copy elision will not take place (without if).
From C++ standard class.copy.elision#1:
is permitted in the following circumstances [...]:
-- in a return statement in a function with a class return type, when the expression is the name of a non-volatile automatic object ([...]) with the same type (ignoring cv-qualification) as the function return type [...]
Technically, when you return a derived class and a slicing operation takes place, the RVO cannot be applied.
Technically RVO works constructing the local object on the returning space on the stack frame.
|--------------|
| local vars |
|--------------|
| return addr |
|--------------|
| return obj |
|--------------|
Generally, a derived class can have a different memory layout than its parent (different size, alignments, ...). So there is no guarantee the local object (derived) can be constructed in the place reserved for the returned object (parent).
Implicit move
Now, what about implicit move?
is the buffer object above guaranteed to be moved from???
In short: no. On the contrary, it is guaranteed the object will be copied!
In this particular case implicit move will not be performed because of slicing.
In short, this happens because the overload resolution fails.
It tries to match against the move-constructor (Buffer::Buffer(Buffer&&)) whereas you have a BufferBuild object). So it fallbacks on the copy constructor.
From C++ standard class.copy.elision#3:
[...] if the type of the first parameter of the selected constructor or the return_value overload is not an rvalue reference to the object's type (possibly cv-qualified), overload resolution is performed again, considering the object as an lvalue.
Therefore, since the first overload resolution fails (as I have said above), the expression will be treated as an lvalue (and not an rvalue), inhibiting the move.
An interesting talk by Arthur O'Dwyer specifically refers to this case. Youtube Video.
Additional Note
On clang, you can pass the flag -Wmove in order to detect this kind of problems.
Indeed for your code:
local variable 'buffer' will be copied despite being returned by name [-Wreturn-std-move]
return buffer;
^~~~~~
<source>:20:11: note: call 'std::move' explicitly to avoid copying
return buffer;
clang directly suggests you to use std::move on the return expression.
1
In standardese, the implicit move is disallowed because "the type of the first parameter of the selected constructor (Buffer&&) is not an rvalue reference to the object's type (BufferBuilder&&)" eel.is/c++draft/class.copy.elision#3.sentence-2
– Oktalist
7 hours ago
Yeah, that what I've written (with other words). But thank you, maybe I can rephrase better and add a reference to the standard.
– Biagio Festa
7 hours ago
You phrased it fine, I just wanted to show the "proof".
– Oktalist
7 hours ago
If my reading of the spec is correct, this modification of the local class should make the compiler move it:struct BufferBuilder BufferBuilder():buffer(std::byte0) Buffer buffer; operator Buffer() && return (Buffer&&)buffer; ;Because there is no "selected constructor" anymore. (EDIT: needs a public constructor in Buffer :/)
– Johannes Schaub - litb
5 hours ago
Clang disagrees with my reading. GCC agrees. Clang says: "<source>:16:7: note: candidate function not viable: no known conversion from 'BufferBuilder' to 'BufferBuilder' for object argument operator Buffer() && return (Buffer&&)buffer; ". I think that this is a bug in Clang.
– Johannes Schaub - litb
4 hours ago
add a comment |
RVO Optimisation
If no copy elision takes place [...]
Actually, copy elision will not take place (without if).
From C++ standard class.copy.elision#1:
is permitted in the following circumstances [...]:
-- in a return statement in a function with a class return type, when the expression is the name of a non-volatile automatic object ([...]) with the same type (ignoring cv-qualification) as the function return type [...]
Technically, when you return a derived class and a slicing operation takes place, the RVO cannot be applied.
Technically RVO works constructing the local object on the returning space on the stack frame.
|--------------|
| local vars |
|--------------|
| return addr |
|--------------|
| return obj |
|--------------|
Generally, a derived class can have a different memory layout than its parent (different size, alignments, ...). So there is no guarantee the local object (derived) can be constructed in the place reserved for the returned object (parent).
Implicit move
Now, what about implicit move?
is the buffer object above guaranteed to be moved from???
In short: no. On the contrary, it is guaranteed the object will be copied!
In this particular case implicit move will not be performed because of slicing.
In short, this happens because the overload resolution fails.
It tries to match against the move-constructor (Buffer::Buffer(Buffer&&)) whereas you have a BufferBuild object). So it fallbacks on the copy constructor.
From C++ standard class.copy.elision#3:
[...] if the type of the first parameter of the selected constructor or the return_value overload is not an rvalue reference to the object's type (possibly cv-qualified), overload resolution is performed again, considering the object as an lvalue.
Therefore, since the first overload resolution fails (as I have said above), the expression will be treated as an lvalue (and not an rvalue), inhibiting the move.
An interesting talk by Arthur O'Dwyer specifically refers to this case. Youtube Video.
Additional Note
On clang, you can pass the flag -Wmove in order to detect this kind of problems.
Indeed for your code:
local variable 'buffer' will be copied despite being returned by name [-Wreturn-std-move]
return buffer;
^~~~~~
<source>:20:11: note: call 'std::move' explicitly to avoid copying
return buffer;
clang directly suggests you to use std::move on the return expression.
RVO Optimisation
If no copy elision takes place [...]
Actually, copy elision will not take place (without if).
From C++ standard class.copy.elision#1:
is permitted in the following circumstances [...]:
-- in a return statement in a function with a class return type, when the expression is the name of a non-volatile automatic object ([...]) with the same type (ignoring cv-qualification) as the function return type [...]
Technically, when you return a derived class and a slicing operation takes place, the RVO cannot be applied.
Technically RVO works constructing the local object on the returning space on the stack frame.
|--------------|
| local vars |
|--------------|
| return addr |
|--------------|
| return obj |
|--------------|
Generally, a derived class can have a different memory layout than its parent (different size, alignments, ...). So there is no guarantee the local object (derived) can be constructed in the place reserved for the returned object (parent).
Implicit move
Now, what about implicit move?
is the buffer object above guaranteed to be moved from???
In short: no. On the contrary, it is guaranteed the object will be copied!
In this particular case implicit move will not be performed because of slicing.
In short, this happens because the overload resolution fails.
It tries to match against the move-constructor (Buffer::Buffer(Buffer&&)) whereas you have a BufferBuild object). So it fallbacks on the copy constructor.
From C++ standard class.copy.elision#3:
[...] if the type of the first parameter of the selected constructor or the return_value overload is not an rvalue reference to the object's type (possibly cv-qualified), overload resolution is performed again, considering the object as an lvalue.
Therefore, since the first overload resolution fails (as I have said above), the expression will be treated as an lvalue (and not an rvalue), inhibiting the move.
An interesting talk by Arthur O'Dwyer specifically refers to this case. Youtube Video.
Additional Note
On clang, you can pass the flag -Wmove in order to detect this kind of problems.
Indeed for your code:
local variable 'buffer' will be copied despite being returned by name [-Wreturn-std-move]
return buffer;
^~~~~~
<source>:20:11: note: call 'std::move' explicitly to avoid copying
return buffer;
clang directly suggests you to use std::move on the return expression.
edited 7 hours ago
answered 7 hours ago
Biagio FestaBiagio Festa
5,9532 gold badges13 silver badges41 bronze badges
5,9532 gold badges13 silver badges41 bronze badges
1
In standardese, the implicit move is disallowed because "the type of the first parameter of the selected constructor (Buffer&&) is not an rvalue reference to the object's type (BufferBuilder&&)" eel.is/c++draft/class.copy.elision#3.sentence-2
– Oktalist
7 hours ago
Yeah, that what I've written (with other words). But thank you, maybe I can rephrase better and add a reference to the standard.
– Biagio Festa
7 hours ago
You phrased it fine, I just wanted to show the "proof".
– Oktalist
7 hours ago
If my reading of the spec is correct, this modification of the local class should make the compiler move it:struct BufferBuilder BufferBuilder():buffer(std::byte0) Buffer buffer; operator Buffer() && return (Buffer&&)buffer; ;Because there is no "selected constructor" anymore. (EDIT: needs a public constructor in Buffer :/)
– Johannes Schaub - litb
5 hours ago
Clang disagrees with my reading. GCC agrees. Clang says: "<source>:16:7: note: candidate function not viable: no known conversion from 'BufferBuilder' to 'BufferBuilder' for object argument operator Buffer() && return (Buffer&&)buffer; ". I think that this is a bug in Clang.
– Johannes Schaub - litb
4 hours ago
add a comment |
1
In standardese, the implicit move is disallowed because "the type of the first parameter of the selected constructor (Buffer&&) is not an rvalue reference to the object's type (BufferBuilder&&)" eel.is/c++draft/class.copy.elision#3.sentence-2
– Oktalist
7 hours ago
Yeah, that what I've written (with other words). But thank you, maybe I can rephrase better and add a reference to the standard.
– Biagio Festa
7 hours ago
You phrased it fine, I just wanted to show the "proof".
– Oktalist
7 hours ago
If my reading of the spec is correct, this modification of the local class should make the compiler move it:struct BufferBuilder BufferBuilder():buffer(std::byte0) Buffer buffer; operator Buffer() && return (Buffer&&)buffer; ;Because there is no "selected constructor" anymore. (EDIT: needs a public constructor in Buffer :/)
– Johannes Schaub - litb
5 hours ago
Clang disagrees with my reading. GCC agrees. Clang says: "<source>:16:7: note: candidate function not viable: no known conversion from 'BufferBuilder' to 'BufferBuilder' for object argument operator Buffer() && return (Buffer&&)buffer; ". I think that this is a bug in Clang.
– Johannes Schaub - litb
4 hours ago
1
1
In standardese, the implicit move is disallowed because "the type of the first parameter of the selected constructor (
Buffer&&) is not an rvalue reference to the object's type (BufferBuilder&&)" eel.is/c++draft/class.copy.elision#3.sentence-2– Oktalist
7 hours ago
In standardese, the implicit move is disallowed because "the type of the first parameter of the selected constructor (
Buffer&&) is not an rvalue reference to the object's type (BufferBuilder&&)" eel.is/c++draft/class.copy.elision#3.sentence-2– Oktalist
7 hours ago
Yeah, that what I've written (with other words). But thank you, maybe I can rephrase better and add a reference to the standard.
– Biagio Festa
7 hours ago
Yeah, that what I've written (with other words). But thank you, maybe I can rephrase better and add a reference to the standard.
– Biagio Festa
7 hours ago
You phrased it fine, I just wanted to show the "proof".
– Oktalist
7 hours ago
You phrased it fine, I just wanted to show the "proof".
– Oktalist
7 hours ago
If my reading of the spec is correct, this modification of the local class should make the compiler move it:
struct BufferBuilder BufferBuilder():buffer(std::byte0) Buffer buffer; operator Buffer() && return (Buffer&&)buffer; ; Because there is no "selected constructor" anymore. (EDIT: needs a public constructor in Buffer :/)– Johannes Schaub - litb
5 hours ago
If my reading of the spec is correct, this modification of the local class should make the compiler move it:
struct BufferBuilder BufferBuilder():buffer(std::byte0) Buffer buffer; operator Buffer() && return (Buffer&&)buffer; ; Because there is no "selected constructor" anymore. (EDIT: needs a public constructor in Buffer :/)– Johannes Schaub - litb
5 hours ago
Clang disagrees with my reading. GCC agrees. Clang says: "<source>:16:7: note: candidate function not viable: no known conversion from 'BufferBuilder' to 'BufferBuilder' for object argument operator Buffer() && return (Buffer&&)buffer; ". I think that this is a bug in Clang.
– Johannes Schaub - litb
4 hours ago
Clang disagrees with my reading. GCC agrees. Clang says: "<source>:16:7: note: candidate function not viable: no known conversion from 'BufferBuilder' to 'BufferBuilder' for object argument operator Buffer() && return (Buffer&&)buffer; ". I think that this is a bug in Clang.
– Johannes Schaub - litb
4 hours ago
add a comment |
The object buffer from make_zeroed_buffer() will destroyed after will be made its copy with the help of Buffers' copy constructor for return value.
New contributor
zooorkin is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.
add a comment |
The object buffer from make_zeroed_buffer() will destroyed after will be made its copy with the help of Buffers' copy constructor for return value.
New contributor
zooorkin is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.
add a comment |
The object buffer from make_zeroed_buffer() will destroyed after will be made its copy with the help of Buffers' copy constructor for return value.
New contributor
zooorkin is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.
The object buffer from make_zeroed_buffer() will destroyed after will be made its copy with the help of Buffers' copy constructor for return value.
New contributor
zooorkin is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.
New contributor
zooorkin is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.
answered 8 hours ago
zooorkinzooorkin
1
1
New contributor
zooorkin is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.
New contributor
zooorkin is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.
add a comment |
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f57028409%2fmove-semantics-in-derived-to-base-class-conversions%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
2
If no copy elision takes place What makes you think that this condition will ever be satisfied? From my recollection, when you explicitly
return std::move(buffer);the compiler (clang) will issue a warning about surpressing copy elision.– Walter
8 hours ago
@Walter What makes you think then that copy elision will always be satisfied?
– El Profesor
8 hours ago
If I remember well, in this very particular case you have to use
return std::move(buffer)– Biagio Festa
8 hours ago
2
No copy elision. The next statement is violated: In a return statement, when the operand is a prvalue of the same class type (ignoring cv-qualification) as the function return type
– S.M.
7 hours ago
1
The buffer object is of type BufferBuilder. Buffer is a public base class of BufferBuilder, so this BufferBuilder object is implicitly converted into a Buffer object. It is not true. The BufferBuilder object is copied implicitly to a Builder object. You confused with pointers to a derived class.
– S.M.
7 hours ago