I am currently quite puzzled with C++17`s guaranteed RVO and its implications. I understand that for NRVO to kick in, I need to make sure that
to return one and the same instance of an object through all possible return path of the function and
to initialize an associated object with the function call on the call side
Consider a most simple decorated class Widget and I want to allocate a pair of Widgets without copies and then return it
#include<iostream>
#include<utility>
struct Widget {
Widget() { std::cerr << "Default ctor" << std::endl; }
explicit Widget(int i) { std::cerr << "custom ctor" << std::endl; }
Widget(Widget const &) { std::cerr << "copy ctor" << std::endl; }
Widget(Widget &&) { std::cerr << "move ctor" << std::endl; }
Widget &operator=(Widget const &) { std::cerr << "copy assign" << std::endl; }
Widget &operator=(Widget &&) { std::cerr << "move assign" << std::endl; }
int i_;
};
auto foo(){
std::pair<Widget,Widget> w; // default construction
auto &[w1,w2] = w;
// do something with w1 and w2
return w;
}
int main(){
auto f = foo();
}
No copy is made but now I tried to use make_pair
auto foo(){
auto w = std::make_pair<Widget,Widget>({},{}); // Awkward syntax and move construction
auto &[w1,w2] = w;
// do something with w1 and w2
return w;
}
Is this really the only possible alternative if I want to use make_pair? Any why is there move construction involved as compared to the first function?
I think the premise of your question is wrong.
Is this really the only possible alternative if I want to use make_pair?
Why would you use std::make_pair here?
auto w = std::make_pair<Widget,Widget>({},{}); // Awkward syntax and move construction
This really is awkward syntax. Let me explain why I think so...
From cppreference on std::make_pair (emphasize mine):
Creates a std::pair object, deducing the target type from the types of arguments.
The only purpose of std::make_pair is to deduce the types of its arguments, as for example in
std::pair<int,int> x;
x = std::pair<int,int>(3,3); // not nice
x = std::make_pair(3,3); // ok
When default constructing a pair and you know its type, you never had to repeat the template parameters
std::pair<int,int> a = std::pair<int,int>(); // awkward
Neither when you want non-default construction (just dont use auto when its only effect is that you have to spell the type elsewhere in the same line of code)
std::pair<int,int> a{5,4}; // no move/copy
Bottomline:
The alternative is to simply not use std::make_pair when you don't need it.
Related
Consider the following C++-Code
#include <iostream>
using namespace std;
struct WrapMe
{
WrapMe() { cout << "WrapMe Default Ctor of: " << this << endl; }
WrapMe(const WrapMe& other) { cout << "WrapMe Copy Ctor of " << this << " from " << &other << endl; }
WrapMe(WrapMe&& other) noexcept { cout << "WrapMe Move Ctor of " << this << " from " << &other << endl; }
~WrapMe() { cout << "Wrap Me Dtor of" << this << endl; }
};
struct Wrapper1
{
WrapMe& data()& { return member; }
WrapMe data()&& { return std::move(member); }
WrapMe member;
};
struct Wrapper2
{
WrapMe& data()& { return member; }
WrapMe&& data()&& { return std::move(member); }
WrapMe member;
};
int main()
{
auto wrapMe1 = Wrapper1().data();
auto wrapMe2 = Wrapper2().data();
}
with the output
WrapMe Default Ctor of: 00000092E7F2F8C4
WrapMe Move Ctor of 00000092E7F2F7C4 from 00000092E7F2F8C4
Wrap Me Dtor of00000092E7F2F8C4
WrapMe Default Ctor of: 00000092E7F2F8E4
WrapMe Move Ctor of 00000092E7F2F7E4 from 00000092E7F2F8E4
Wrap Me Dtor of00000092E7F2F8E4
[...]
Which is the correct way to move from the WrapMe member: Like Wrapper1 (return by value) or like Wrapper2 (return by rvalue-reference) does? Or are both ways equivalent here, as the ouput suggests? If not, why?
WrapMe&& data()&& { return std::move(member); }
This doesn't actually move anything. It just returns a rvalue reference to the member. For example I could do
auto&& wrapMe2 = Wrapper2().data();
and now wrapMe2 will be a dangling reference or
auto w = wrapper2();
auto&& wrapMe2 = std::move(x).data();
Now I have a reference to the member of w without w having changed at all.
Only because the move constructor of WrapMe is called to initialize the wrapMe2 object in the original line, a move operations actually takes place.
The version
WrapMe data()&& { return std::move(member); }
calls the move constructor already to construct the return value. The returned value will never refer to the original object or its member.
The line
auto wrapMe1 = Wrapper1().data();
then calls the move constructor again to initialize wrapMe1 from the return value of .data(). The compiler is however allowed to elide this second move constructor call and instead construct wrapMe1 directly from the expression in the return statement. This is why you see the same result.
With C++17 or later this elision would even be mandatory.
I don't know your use case, so I cannot be sure what the correct approach is, but just guessing on what you want to do:
For consistency between the two overloads, I would use the reference-returning version. Having data provide modifiable access to the member for lvalues, but not for rvalues, would be confusing.
However, you don't really need a member function to do this. Through data the caller has full control over the member anyway, so you could just make the member public from the start and then it could be used in the same way directly.
Currently, I'm storing a collection of std::unique_ptrs to heap allocated objects of a polymorphic type:
struct Foo {
virtual ~Foo() = default;
};
Collection<std::unique_ptr<Foo>> foos;
The basic interface I need is putting / taking owners of Foo to / from foos. The objects stored in foos are never supposed to be nullptr so I'd like to replace runtime assert(owner_taken) with compile-time checks. Moreover, I would like to be capable of using non-null owners in the context where a nullable one may be expected.
Probably, I need to store something like unique_ref but how would I extract one from foos? I don't want a copy, I want the stored object itself, so owner->clone() isn't a solution. Neither I can std::move(owner) because the state of a "unique reference" would be invalid afterwards.
Is there a clean design decision?
Is there a never-null unique owner of heap allocated objects?
There is no such type in the standard library.
It is possible to implement such type. Simply define a type with unique_ptr member and mark the default constructor deleted. You can mark constructor from std::nullptr_t deleted also so that construction from nullptr will be prevented at compile time.
Whether you construct from an external pointer, or allocate in the constructor, there is no alternative for checking for null at runtime.
Reading your question, I interpret the following requirements:
You don't want to copy or move the object itself (Foo)
You don't want a wrapper which has some sort of empty state which excludes move semantics
The object itself (Foo) should be stored on the heap such that its lifetime is independent of the control flow
The object itself (Foo) should be deleted once it is not used any more
As construction / destruction, copy and move are the only ways to get objects into / out of a container, the only thing left is a wrapper object which is copied when moved into / out of the container.
You can create such an object yourself as follows:
// LICENSE: MIT
#include <memory>
#include <utility>
template<typename T>
class shared_ref {
public:
template<typename ...Args>
shared_ref(Args&&... args)
: data{new T(std::forward<Args>(args)...)}
{}
shared_ref(shared_ref&&) = delete;
shared_ref& operator=(shared_ref&&) = delete;
T& get() noexcept {
return *data;
}
const T& get() const noexcept {
return *data;
}
operator T&() noexcept {
return get();
}
operator const T&() const noexcept {
return get();
}
void swap(shared_ref& other) noexcept {
return data.swap(other);
}
private:
std::shared_ptr<T> data;
};
template<class T>
void swap(shared_ref<T>& lhs, shared_ref<T>& rhs) noexcept {
return lhs.swap(rhs);
}
I leave it as an exercise to the reader to add support for Allocator, Deleter, operator[], implicit conversion contructor to base classes.
This can then be used as follows:
#include <iostream>
int main() {
shared_ref<int> r; // default initialized
std::cout << "r=" << r << std::endl;
r = 5; // type conversion operator to reference
std::cout << "r=" << r << std::endl;
shared_ref<int> s{7}; // initialized with forwarded arguments
std::cout << "s=" << s << std::endl;
std::swap(r, s);
std::cout << "r=" << r << ", " << "s=" << s << std::endl;
s = r; // copy assignment operator
std::cout << "s=" << s << std::endl;
const shared_ref<int> t{s}; // copy constructor
std::cout << "t=" << t << std::endl;
//t = 8; // const ref from a const object is immutable
return 0;
}
I have a situation which I cannot wrap my head around. I define a non-copy-able struct and want to in-place construct it in a tuple. If I do so, it is copied. I thought the problem may be std::make_tuple, but it isn't.
If instead I construct the object, then move it in the tuple, things work as expected.
Demonstration (I use std::exit to prevent the output of normal destruction).
#include <tuple>
#include <iostream>
#include <cstdlib>
struct MoveMe {
MoveMe(size_t s) : size(s) {}
MoveMe(MoveMe&&) = default;
MoveMe(const MoveMe&) = delete;
MoveMe& operator=(MoveMe&&) = default;
MoveMe& operator=(const MoveMe&) = delete;
~MoveMe() {
std::cout << "Destroyed" << std::endl;
}
size_t size = 0;
};
int main(int, char**) {
std::cout << "Constructed in-place." << std::endl;
auto tuple = std::make_tuple(MoveMe{10});
std::cout << std::endl << "Other tests." << std::endl;
std::tuple<MoveMe> tuple2 = std::tuple<MoveMe>(MoveMe{10});
std::cout << std::endl << "Moved." << std::endl;
MoveMe obj{10};
auto tuple3 = std::make_tuple(std::move(obj));
std::exit(0);
// return 0;
}
Outputs
Constructed in-place.
Destroyed
Other tests.
Destroyed
Moved.
Any ideas why that is? My understanding of rvalues is basic, so my guess is I'm missing something obvious. Thank you.
It isn't copied it is still moved.
In your case, you're creating a temporary (MoveMe{10}) that is then used to move construct the instance of MoveMe in the tuple. After the instance of MoveMe in the tuple is move constructed, the temporary that was moved is destroyed.
You can directly construct the MoveMe object in the tuple by forwarding the arguments
std::cout << "Directly Constructed" << std::endl;
auto tuple = std::tuple<MoveMe>(10);
Which will not result in a temporary being created then destroyed.
This question already has answers here:
Why is value taking setter member functions not recommended in Herb Sutter's CppCon 2014 talk (Back to Basics: Modern C++ Style)?
(4 answers)
Closed 7 years ago.
Assume I have the following class, which has a method set_value. Which implementation is better?
class S {
public:
// a set_value method
private:
Some_type value;
};
Pass by value, then move
void S::set_value(Some_type value)
{
this->value = std::move(value);
}
Define two overloaded methods
void S::set_value(const Some_type& value)
{
this->value = value;
}
void S::set_value(Some_type&& value)
{
this->value = std::move(value);
}
The first approach requires definition of one method only while the second requires two.
However, the first approach seems to be less efficient:
Copy/Move constructor for the parameter depending on the argument passed
Move assignment
Destructor for the parameter
While for the second approach, only one assignment operation is performed.
Copy/Move assignment depending on which overloaded method is called
So, which implementation is better? Or does it matter at all?
And one more question: Is the following code equivalent to the two overloaded methods in the second approach?
template <class T>
void S::set_value(T&& value)
{
this->value = std::forward<T>(value);
}
The compiler is free to elide (optimise away) the copy even if there would be side effects in doing so. As a result, passing by value and moving the result actually gives you all of the performance benefits of the two-method solution while giving you only one code path to maintain. You should absolutely prefer to pass by value.
here's an example to prove it:
#include <iostream>
struct XYZ {
XYZ() { std::cout << "constructed" << std::endl; }
XYZ(const XYZ&) {
std::cout << "copy constructed" << std::endl;
}
XYZ(XYZ&&) noexcept {
try {
std::cout << "move constructed" << std::endl;
}
catch(...) {
}
}
XYZ& operator=(const XYZ&) {
std::cout << "assigned" << std::endl;
return *this;
}
XYZ& operator=(XYZ&&) {
std::cout << "move-assigned" << std::endl;
return *this;
}
};
struct holder {
holder(XYZ xyz) : _xyz(std::move(xyz)) {}
void set_value(XYZ xyz) { _xyz = std::move(xyz); }
void set_value_by_const_ref(const XYZ& xyz) { _xyz = xyz; }
XYZ _xyz;
};
using namespace std;
auto main() -> int
{
cout << "** create named source for later use **" << endl;
XYZ xyz2{};
cout << "\n**initial construction**" << std::endl;
holder h { XYZ() };
cout << "\n**set_value()**" << endl;
h.set_value(XYZ());
cout << "\n**set_value_by_const_ref() with nameless temporary**" << endl;
h.set_value_by_const_ref(XYZ());
cout << "\n**set_value() with named source**" << endl;
h.set_value(xyz2);
cout << "\n**set_value_by_const_ref() with named source**" << endl;
h.set_value_by_const_ref(xyz2);
return 0;
}
expected output:
** create named source for later use **
constructed
**initial construction**
constructed
move constructed
**set_value()**
constructed
move-assigned
**set_value_by_const_ref() with nameless temporary**
constructed
assigned
**set_value() with named source**
copy constructed
move-assigned
**set_value_by_const_ref() with named source**
assigned
note the absence of any redundant copies in the copy/move versions but the redundant copy-assignment when calling set_value_by_const_ref() with nameless temporary. I note the apparent efficiency gain of the final case. I would argue that (a) it's a corner case in reality and (b) the optimiser can take care of it.
my command line:
c++ -o move -std=c++1y -stdlib=libc++ -O3 move.cpp
Reading Meyers new book I found something very similar to this:
// compile with
// g++-4.8 --std=c++11 -Wall main3.cc && ./a.out
#include <iostream>
#include <vector>
class Widget
{
public:
using DType = std::vector<int>;
DType& data() & // lvalue
{
std::cout << "data (lvalue) : " << &data_[0] << std::endl;
return data_;
};
DType data() && // rvalue
{
std::cout << "data (rvalue) : " << &data_[0] << std::endl;
return std::move(data_);
};
// Please Note
// int parameter is here to make the overloading possible
// in a single class
DType&& data(int) &&
{
std::cout << "data (rvalue ref): " << &data_[0] << std::endl;
return std::move(data_);
};
private:
DType data_ { 0 };
};
Widget getWidget() { return Widget(); }
int main(int argc, char *argv[])
{
Widget w1;
std::vector<int> d1 = w1.data();
std::cout << "d1 copied : " << &d1[0] << std::endl;
std::vector<int> d2 = getWidget().data();
std::cout << "d2 moved : " << &d2[0] << std::endl;
std::vector<int> d3 = getWidget().data(0);
std::cout << "d3 moved : " << &d3[0] << std::endl;
return 0;
}
My point is very simple:
On my box as I would expect I have these results
data (lvalue) : 0x8e28008
d1 copied : 0x8e28018
data (rvalue) : 0x8e28028
d2 moved : 0x8e28028
data (rvalue ref): 0x8e28038
d3 moved : 0x8e28038
So first vector was copied while the second and third were moved.
You can have two different signature to achieve move operation:
One returning an rvalue
DType data() && // rvalue
and one returing a rvalue reference
DType&& data() &&
They achieve the same result: are there any differences I cannot see ? What is the "best" one?
You can have two different signature to achieve move operation:
That is wrong.
The first signature, the one returning DType, performs a move into the return value. The second signature, the one returning DType&& simply returns a reference. It doesn't move anything.
The move happens in the other code, specifically the part with std::vector<int> d3 =. Initializing a vector from an xvalue performs a move. That is what does the move, not the function. However, other kinds of operations won't perform a move:
// no move, just binding the member to a reference
std::vector<int>&& d3 = getWidget().data(0);
Using the first function, however, the move always happens:
// move into a temporary, and bind *that* to a reference
std::vector<int>&& d2 = getWidget().data();
The second signature is dangerous. It is easy to accidentally return a reference to a temporary with it. It is easy to write misleading client code where you think something got moved out but didn't. There is one sensible use case for returning rvalue references, and that use case was already taken care of by the standard library in the forms of std::move and std::forward.