seeking general guidance on common design practice here in > c++14 world.
Is const std::string& mName the most appropriate type here? it has the least copies, but is it good style?
I feel like a ref implies some other alias that is or could be in unknown state at some point.
Is it best style for one to make the defensive copy? and go const std::string mName instead? or if that is the case take the copy in the parameter const std::string aName and keep the & in the member field?
whats the most common practice for parameter and member storage and what are the factors that push the decision in other ways?
class Machine {
private:
const std::string& mName;
public:
Machine(const std::string& aName) : mName(aName) {
std::cout << &aName << std::endl;
std::cout << &mName << std::endl;
std::cout << std::endl;
}
};
int main() {
std::string x = "Number 1";
std::cout << &x << std::endl;
Machine m(x);
Machine m2("Number 6");
}
EDIT *
This feels better and also eliminate extra copy on temporaries I think??
class Machine {
private:
const std::string mName;
public:
Machine(const std::string&& aName) : mName(aName) {
std::cout << "1:: " << &aName << std::endl;
std::cout << "1:: " << &mName << std::endl;
}
Machine(const std::string& aName) : mName(aName) {
std::cout << "2:: " << &aName << std::endl;
std::cout << "2:: " << &mName << std::endl;
}
const std::string& name() const {
return mName;
}
};
int main() {
std::string x = "Number 1";
std::cout << &x << std::endl;
Machine m(x);
Machine m2("Number 6");
}
If you're going to store the constructor parameter in your object, then take the object by value. If the caller passes an rvalue, it will be move constructed in. If the user passes an lvalue, you were going to have to create a copy when you copy it into your object, anyhow, so just do it a step earlier.
This results in the cleanest, easiest to understand code:
class Machine {
private:
const std::string mName;
public:
Machine(const std::string aName) : mName(std::move(aName)) {
std::cout << "1:: " << &aName << std::endl; // this has been moved out of...
std::cout << "1:: " << &mName << std::endl;
}
const std::string& name() const {
return mName;
}
};
int main() {
Machine m(function_that_returns_a_string()); // move constructed all the way in - no copies made
string s;
Machine m2(s); // only one copy is made - when calling constructor, but it's then move constructed into the object
}
There are times when objects are still too expensive to move construct, at which point making two versions taking an rvalue and lvalue reference will be a win, but for most cases, the style above is the best approach.
Related
This question already has answers here:
Why can we use `std::move` on a `const` object?
(6 answers)
Why does calling std::move on a const object call the copy constructor when passed to another object?
(2 answers)
Closed 3 months ago.
Just when I thought I was clear about c++ rvalue references, I came across this code:
class my_class {
public:
my_class() { }
auto set(const std::string& s)
{
std::cout << "set(const std::string&) - copying unmutable lvalue" << std::endl;
m_s = s;
}
auto set(std::string& s)
{
std::cout << "set(std::string& s) - copying mutable lvalue" << std::endl;
m_s = s;
}
auto set(std::string&& s)
{
std::cout << "set(std::string&& s) - moving mutable rvalue" << std::endl;
m_s = std::move(s);
}
auto set(const std::string&& s)
{
std::cout << "set(const std::string&& s) - moving unmutable rvalue" << std::endl;
m_s = std::move(s);
}
private:
std::string m_s;
};
int main()
{
my_class c;
std::string lvalue{"lvalue"};
const std::string const_lvalue{"const lvalue"};
std::cout << "1. c.set(lvalue)" << std::endl;
c.set(lvalue);
std::cout << "2. c.set(std::move(lvalue))" << std::endl;
c.set(std::move(lvalue));
std::cout << "3. c.set(const_lvalue)" << std::endl;
c.set(const_lvalue);
std::cout << "4. c.set(std::move(const_lvalue))" << std::endl;
c.set(std::move(const_lvalue));
std::cout << "5. c.set(std::string{\"rvalue\"})" << std::endl;
c.set(std::string{"rvalue"});
// compile error
// c.set(const std::string{"const rvalue"});
return 0;
}
Output:
1. c.set(lvalue)
set(std::string& s) - copying mutable lvalue
2. c.set(std::move(lvalue))
set(std::string&& s) - moving mutable rvalue
3. c.set(const_lvalue)
set(const std::string&) - copying unmutable lvalue
4. c.set(std::move(const_lvalue))
set(const std::string&& s) - moving unmutable rvalue
5. c.set(std::string{"rvalue"})
set(std::string&& s) - moving mutable rvalue
I don't understand 4. Why do we bind to a const rvalue reference and how does c++ allow to invoke std::move() on this thing?
I have found this answer to the question "Does a const reference prolong the life of a temporary?", which states:
Only local const references prolong the lifespan.
I'm afraid my standardese is not up to scratch to know whether foo, below, is a local const reference or not.
Does my const std::string& foo below prolong the lifetime of the temporary std::string function argument created in the call to get_or, or do I have a dangling reference?
#include <iostream>
#include <boost/optional.hpp>
struct Foo
{
const std::string& get_or(const std::string& def)
{
return str ? str.get() : def;
}
boost::optional<std::string> str;
};
int main()
{
Foo f;
const std::string& foo = f.get_or("hello world");
std::cout << foo << '\n';
}
const& won't extend lifetimes in that situation. Consider the example here that constructs a temporary and then attempts to print it: it's using the same constructs as your code, but I've altered it to make object construction and destruction more explicit to the user.
#include <iostream>
struct reporting {
reporting() { std::cout << "Constructed" << std::endl;}
~reporting() { std::cout << "Destructed" << std::endl;}
reporting(reporting const&) { std::cout << "Copy-Constructed" << std::endl;}
reporting(reporting &&) { std::cout << "Move-Constructed" << std::endl;}
reporting & operator=(reporting const&) { std::cout << "Copy-Assigned" << std::endl; return *this;}
reporting & operator=(reporting &&) { std::cout << "Move-Assigned" << std::endl; return *this;}
void print() const {std::cout << "Printing." << std::endl;}
};
const reporting& get_or(const reporting& def)
{
return def;
}
int main()
{
const reporting& foo = get_or(reporting{});
foo.print();
return 0;
}
Output:
Constructed
Destructed
printing.
Note how the object is destroyed before printing. is displayed.
You might be wondering why the code still completes with no visible errors: it's the result of Undefined Behavior. The object in question doesn't exist, but because it doesn't depend on state to invoke its method, the program happens to not crash. Other, more complicated examples should carry no guarantee that this will work without crashing or causing other, unexpected behavior.
Incidentally, things are a little different if the temporary is bound directly to the const&:
#include <iostream>
struct reporting {
reporting() { std::cout << "Constructed" << std::endl;}
~reporting() { std::cout << "Destructed" << std::endl;}
reporting(reporting const&) { std::cout << "Copy-Constructed" << std::endl;}
reporting(reporting &&) { std::cout << "Move-Constructed" << std::endl;}
reporting & operator=(reporting const&) { std::cout << "Copy-Assigned" << std::endl; return *this;}
reporting & operator=(reporting &&) { std::cout << "Move-Assigned" << std::endl; return *this;}
void print() const {std::cout << "printing." << std::endl;}
};
const reporting& get_or(const reporting& def)
{
return def;
}
int main()
{
const reporting& foo = reporting{};
foo.print();
return 0;
}
Output:
Constructed
printing.
Destructed
See how the object isn't destroyed until after it is used. In this situation, the object survives until the end of scope.
You passed the string through too many references.
Binding the temporary string to the def parameter of get_or extends the lifetime of the string to the end of the full expression containing the function call, but binding def to the return value of get_or and binding the return value of get_or to foo do not extend the lifetime further. The string is dead by the time you try to print it.
The "temporary" in question is the std::string-object created when calling get_or with a parameter of type const char*. The lifetime of this temporary object is limited with the end of function get_or, and the fact that you return a reference to this temporary and assign it afterwards does not prolong the lifetime. See the following code which uses a simple "custom" string class, which couts construction and destruction:
class MyString {
public:
MyString (const char* str) {
m_str = strdup(str);
cout << "constructor MyString - '" << m_str << "'" << endl;
}
~MyString() {
cout << "destructor MyString - '" << m_str << "'" << endl;
free(m_str);
}
char *m_str;
};
struct Foo
{
const MyString& get_or(const MyString& def)
{
cout << "Foo::get_or with '" << def.m_str << "'" << endl;
return def;
}
};
int main()
{
Foo f;
const MyString& foo = f.get_or("hello world");
cout << "usage of foo?" << endl;
}
Output:
constructor MyString - 'hello world'
Foo::get_or with 'hello world'
destructor MyString - 'hello world'
usage of foo?
Note that the destructor is called before you will have the chance to use foo.
The situation is different if you assign a reference to a temporary directly. Again, the lifetime is until the end of the function main, but it will be used in main and not in any function calling main:
const MyString& foo2 = MyString("hello world2");
cout << "usage of foo..." << endl;
Then the output will be:
constructor MyString - 'hello world2'
usage of foo...
destructor MyString - 'hello world2'
I am new to C++. Can someone please let me know what is wrong with the following code segment -
class Person {
public:
const std::string& name;
Person(const std::string& s): name(s) {}
void dump(void) const {
cout << name << endl;
//cout << &name << endl;
}
};
std::map<std::string, std::shared_ptr<Person>> plist;
std::string namestr = "Hoo";
std::shared_ptr<Person> r1(std::make_shared<Person>("Dull"));
plist.insert({"Key1", r1});
auto u = plist.find("Key1");
shared_ptr<Person> v = u->second;
v->dump();
plist.erase(plist.find("Key1"));
My intention is to create a database of Person objects and I was trying to use shared_ptr for that.
v->dump() causes a segmentation fault. However, if I use the 'namestr' variable instead of the string literal "Dull" then the v->dump() appears to work correctly, i.e. the following -
std::shared_ptr<Person> r1(std::make_shared<Person>(namestr));
Also, the following way also seems to work even though I use a string literal in the intializer.
std::shared_ptr<Person> r1(new Person("Dull"));
Pointers to the mistake I am making would be much appreciated!
class Person {
public:
const std::string& name;
Person(const std::string& s): name(s) {}
void dump(void) const {
cout << name << endl;
//cout << &name << endl;
}
};
this is storing a reference to a string whose life time is not guaranteed. You should do
class Person {
public:
const std::string name;
Person(const std::string& s): name(s) {}
void dump(void) const {
cout << name << endl;
//cout << &name << endl;
}
};
You code fails because "Dull" created a temporary string that went out of scope immediately
Consider this code:
#include <iostream>
#include <functional>
using namespace std;
using namespace std::placeholders;
typedef function<void(const int&)> SomeFunc;
class X {
public:
X(string name):name_(name)
{ cout << "ctor " << name_ << endl; }
~X()
{
cout << "dtor " << name_ << endl;
name_ = "empty";
}
SomeFunc
getSomeFunc()
{ return bind(&X::someMethod, this, _1); }
private:
string name_;
void
someMethod(const int& a)
{
cout << name_ << " some method with " << a << endl;
}
};
int main()
{
SomeFunc f;
{
shared_ptr<X> x(new X("Object"));
f = x->getSomeFunc();
f(1);
}
f(2);
return 0;
}
Sometimes, output gives me this:
ctor Object
Object some method with 1
dtor Object
empty some method with 2
other times this:
ctor Object
Object some method with 1
dtor Object
some method with 2
In real world, it would most probably give me crashes once deallocated object tries to access it's attributes.
So here is a question - as function does not guarantee holding a reference to the object which method it's pointing to, what is the best practice to avoid crashes when function is called after referenced object was already deallocated?
One of the solutions I might think of - maintain a special flag bool deallocated_ inside object and check it inside the method which might be called after deallocation. However, I suspect, it's not reliable either.
UPDATE (from comments):
The real reason I need this workaround is the library that takes function as a parameter. This library operates asynchronously and I have no control over function objects passed into it. That's why when my object is deallocated, library still can invoke callbacks using originally passed function which leads to a crash.
Your object is being held by a shared_ptr, so you can use a lambda to close over the shared_ptr:
auto func = [ptr](const int &p){ ptr->someMethod(p); };
You'll need to use shared_from_this to get ptr within the class.
Here's a full example that works:
#include <iostream>
#include <functional>
#include <memory>
using namespace std;
using namespace std::placeholders;
typedef function<void(const int&)> SomeFunc;
class X : public enable_shared_from_this<X> {
public:
X(string name) : name_(name) {
cout << "ctor " << name_ << endl;
}
~X() {
cout << "dtor " << name_ << endl;
name_ = "empty";
}
SomeFunc getSomeFunc() {
auto ptr = shared_from_this();
return [ptr](const int &a){ ptr->someMethod(a); };
}
private:
string name_;
void someMethod(const int& a) {
cout << name_ << " some method with " << a << endl;
}
};
int main()
{
SomeFunc f;
{
shared_ptr<X> x(new X("Object"));
f = x->getSomeFunc();
f(1);
}
f(2);
return 0;
}
The output looks like this:
ctor Object
Object some method with 1
Object some method with 2
dtor Object
Sulution 1) Using weak_ptr + lambda (almost the same as from b4hand, but it won't force your class beeing alive)
Inherit your class from std::enable_shared_from_this
class X : public enable_shared_from_this<X>
and change getSomeFunc to something like this:
SomeFunc getSomeFunc()
{
weak_ptr<X> weak = shared_from_this();
return [weak, this](const int& a){
shared_ptr<X> shared = weak.lock();
if (shared)
{
this->someMethod(a);
}
};
}
output:
ctor Object
Object some method with 1
dtor Object
more details here and here.
Solution 2) A bit of crazy code + lambda
If you can't or don't want to use shared/weak ptrs, you can do it this way:
#include <memory>
#include <functional>
#include <iostream>
#include <memory>
#include <string>
#include <set>
using namespace std;
typedef function<void(const int&)> SomeFunc;
class X {
private:
static set<X*> _aliveInstanties;
public:
X(string name) :name_(name)
{
_aliveInstanties.insert(this);
cout << "ctor " << name_ << endl;
}
~X()
{
_aliveInstanties.erase(_aliveInstanties.find(this));
cout << "dtor " << name_ << endl;
name_ = "empty";
}
SomeFunc getSomeFunc()
{
return [this](const int& a)
{
if (_aliveInstanties.find(this) != _aliveInstanties.end())
{
this->someMethod(a);
}
};
}
private:
string name_;
void someMethod(const int& a)
{
cout << name_ << " some method with " << a << endl;
}
};
You can create a class that holds a function pointer and a shared_ptr to the object. The shared_ptr to the object guarantees the object won't be destroyed until your function class is destroyed.
Another solution without using lambda is to derive from enable_shared_from_this and pass shared_from_this in getSomeFunc method:
class X : public enable_shared_from_this<X> {
public:
X(string name):name_(name)
{ cout << "ctor " << name_ << endl; }
~X()
{
cout << "dtor " << name_ << endl;
name_ = "empty";
}
SomeFunc
getSomeFunc()
{
return bind(&X::someMethod, shared_from_this(), _1);
}
private:
string name_;
void
someMethod(const int& a)
{
cout << name_ << " some method with " << a << endl;
}
};
This, however, will hold object until all callbacks are released.
Say I have this function:
template <class A>
inline A f()
{
A const r(/* a very complex and expensive construction */);
return r;
}
Is it a good idea to declare r const, since a const variable cannot be moved? Note that the returned value is not const. The qualm I am grappling is, that r truly is const, but it may not be a good idea to declare it as such. Yet the qualifier should be helping the compiler generate better code.
As demonstrated here, NRVO elides the copy of r implied by the line return r;
#include <iostream>
struct A {
const char* name;
A( const char* name_ ):name(name_) { std::cout << "created " << name << "\n"; }
A(A const&){ std::cout << "copied " << name << "\n"; }
A(A &&){ std::cout << "moved " << name << "\n"; }
};
A f() {
std::cout << "start of f()\n";
A const r("bob");
std::cout << "body of f()\n";
return r;
}
int main() {
A x = f();
}
And the copy in main is also elided.
If you block NRVO and RVO in some other way (for instance using the flag -fno-elide-constructors when compiling with GCC), the const can cause your object to be copied instead of moved. You can see this if we remove the copy constructor from A:
#include <iostream>
struct A {
const char* name;
A( const char* name_ ):name(name_) { std::cout << "created " << name << "\n"; }
//A(A const&){ std::cout << "copied " << name << "\n"; }
A(A &&){ std::cout << "moved " << name << "\n"; }
};
A f() {
std::cout << "start of f()\n";
A const r("bob");
std::cout << "body of f()\n";
return r;
}
int main() {
A x = f();
}
the code no longer compiles. While the copy constructor isn't executed so long as NRVO occurs, its existence is required by your const local variable.
Now, NRVO requires a few things, such as a single variable which is returned along every single execution path of the function in question: if you ever "abort" and do a return A(), NRVO is blocked, and your const local variable suddenly forces a copy at all return sites.
If class A is under your control, and you want to return const objects by move, you can do
mutable bool resources_were_stolen = false;
and set that to true in a const move constructor
A(const A&& other) { ...; other.resources_were_stolen = true; }
~A() { if (!resources_were_stolen) ... }
Actually, the destructor probably would become if (resources_were_stolen) some_unique_ptr.release();, using the fact that objects lose their const-ness during construction and destruction.