C++11 rvalue calls destructior twice - c++

I'm trying to make a class runner (run a class at a fixed time freq), which runs a class in another thread, and can be controlled (like pause, resume, stop) from main thread.
So I want to take advantage of C++11's Functor and other features. But I have a strange problem, the Functor's destructor passed into Runner has been called twice.
#include <iostream>
#include <chrono>
#include <thread>
using namespace std;
class Runner {
public:
typedef function<bool()> fn_t;
Runner(fn_t &&fn) : fn_(move(fn)), thread_(Thread, ref(*this)) {
cout << "Runner" << endl;
}
~Runner() {
cout << "~Runner" << endl;
thread_.join();
}
private:
fn_t fn_;
thread thread_;
static void Thread(Runner &runner) {
while (runner.fn_()) {
cout << "Running" << endl;
this_thread::sleep_for(chrono::milliumseconds(1));
}
}
};
class Fn {
public:
Fn() : count(0) {
cout << "Fn" << endl;
}
~Fn() {
cout << "~Fn" << endl;
}
bool operator()() {
return (++count < 5);
}
private:
int count;
};
int main (int argc, char const* argv[])
{
Fn fn;
Runner runner(move(fn));
return 0;
}
outpus:
Fn
Runner
~Fn
~Runner
Running
Running
Running
Running
Running
~Fn
~Fn
and if I change
Fn fn;
Runner runner(move(fn));
to
Runner runner(Fn());
the program outpus nothing and stalls. I have tried to disable compiling optimization, nothing changes. Any explanation?
How can I fix this or do the samething in other method? Should I implement this class like std::async / std::thread?
Update to Runner runner(Fn())
This statement was interrupted as a function declaration.
Runner runner((Fn())) solved problem.
Thanks for all comments and answers. After look into rvalue, seems I have misunderstand the meaning of rvalue reference from ground 0. I will try some other ways.
Final Solution for this problem
#include <iostream>
#include <chrono>
#include <thread>
#include <vector>
using namespace std;
template<typename T, typename... Args>
class Runner {
public:
Runner(Args&&... args) :
t(forward<Args>(args)...),
thread_(Thread, ref(*this)) {
cout << "Runner" << endl;
}
~Runner() {
cout << "~Runner" << endl;
thread_.join();
}
private:
T t;
thread thread_;
static void Thread(Runner &runner) {
while (runner.t()) {
cout << "Running" << endl;
this_thread::sleep_for(chrono::milliseconds(100));
}
}
};
class Fn {
public:
Fn() : count(0) {
cout << "Fn" << endl;
}
~Fn() {
cout << "~Fn" << endl;
}
bool operator()() {
return (count++ < 5);
}
private:
int count;
};
int main (int argc, char const* argv[])
{
//vector<Fn> fns;
//fns.emplace_back(Fn());
Runner<Fn> runner;
return 0;
}
outpus:
Fn
Runner
~Runner
Running
Running
Running
Running
Running
~Fn

Use std::move:
Runner(fn_t &&fn) : fn_(std::move(fn)), thread_(Thread, ref(*this)) {
/*....*/
}
You need to explicitly use std::move, otherwise it will be treated as a const reference. You could also use std::forward:
Runner(fn_t &&fn) : fn_(std::forward<fn_t>(fn)), thread_(Thread, ref(*this)) {
/*....*/
}

First of all, you shouldn't be taking r-value reference arguments for the most part, except in your own move constructors. As you have it, there is no way to pass l-values of std::function<bool()> into the constructor of Runner.
int main()
{
Fn fn;
std::function<bool()> func(fn);
Runner runner(func); // this is illegal
}
Maybe I'm just not creative enough, but I can't imagine any valid reason which you would want to prevent such a thing.
You should let std::function take care of its own copying/moving. When you need a copy of an object, take your parameter by value. If the function is passed an r-value, then it will be move constructed. If it is passed an l-value, then it will be copy constructed. Then, in your Runner constructor, you can move the value into the member object, as fontanini showed.
None of this is guaranteed to reduce destructor calls though, because when you move an object, you're still creating a second object, and will have to destroy a second object. In order to see fewer destructions, copy elision would have to happen, which actually does avoid the creation of multiple objects. But unlike moving, that's an implementation issue that's not guaranteed to come into effect in all the situations where you would hope.

Related

How to write a constructor for non-named class [duplicate]

Is there a way to declare a constructor or a destructor in an unnamed class? Consider the following
void f()
{
struct {
// some implementation
} inst1, inst2;
// f implementation - usage of instances
}
Follow up question : The instances are ofcourse constructed (and destroyed) as any stack based object. What gets called? Is it a mangled name automatically assigned by the compiler?
The simplest solution is to put a named struct instance as a member in the unnamed one, and put all of the functionality into the named instance. This is probably the only way that is compatible with C++98.
#include <iostream>
#include <cmath>
int main() {
struct {
struct S {
double a;
int b;
S() : a(sqrt(4)), b(42) { std::cout << "constructed" << std::endl; }
~S() { std::cout << "destructed" << std::endl; }
} s;
} instance1, instance2;
std::cout << "body" << std::endl;
}
Everything that follows requires C++11 value initialization support.
To avoid the nesting, the solution for the construction is easy. You should be using C++11 value initialization for all members. You can initialize them with the result of a lambda call, so you can really execute arbitrarily complex code during the initialization.
#include <iostream>
#include <cmath>
int main() {
struct {
double a { sqrt(4) };
int b { []{
std::cout << "constructed" << std::endl;
return 42; }()
};
} instance1, instance2;
}
You can of course shove all the "constructor" code to a separate member:
int b { [this]{ constructor(); return 42; }() };
void constructor() {
std::cout << "constructed" << std::endl;
}
This still doesn't read all that cleanly, and conflates the initialization of b with other things. You could move the constructor call to a helper class, at the cost of the empty class still taking up a bit of space within the unnamed struct (usually one byte if it's the last member).
#include <iostream>
#include <cmath>
struct Construct {
template <typename T> Construct(T* instance) {
instance->constructor();
}
};
int main() {
struct {
double a { sqrt(4) };
int b { 42 };
Construct c { this };
void constructor() {
std::cout << "constructed" << std::endl;
}
} instance1, instance2;
}
Since the instance of c will use some room, we might as well get explicit about it, and get rid of the helper. The below smells of a C++11 idiom, but is a bit verbose due to the return statement.
struct {
double a { sqrt(4) };
int b { 42 };
char constructor { [this]{
std::cout << "constructed" << std::endl;
return char(0);
}() };
}
To get the destructor, you need the helper to store both the pointer to an instance of the wrapped class, and a function pointer to a function that calls the destructor on the instance. Since we only have access to the unnamed struct's type in the helper's constructor, it's there that we have to generate the code that calls the destructor.
#include <iostream>
#include <cmath>
struct ConstructDestruct {
void * m_instance;
void (*m_destructor)(void*);
template <typename T> ConstructDestruct(T* instance) :
m_instance(instance),
m_destructor(+[](void* obj){ static_cast<T*>(obj)->destructor(); })
{
instance->constructor();
}
~ConstructDestruct() {
m_destructor(m_instance);
}
};
int main() {
struct {
double a { sqrt(4) };
int b { 42 };
ConstructDestruct cd { this };
void constructor() {
std::cout << "constructed" << std::endl;
}
void destructor() {
std::cout << "destructed" << std::endl;
}
} instance1, instance2;
std::cout << "body" << std::endl;
}
Now you're certainly complaining about the redundancy of the data stored in the ConstructDestruct instance. The location where the instance is stored is at a fixed offset from the head of the unnamed struct. You can obtain such offset and wrap it in a type (see here). Thus we can get rid of the instance pointer in the ConstructorDestructor:
#include <iostream>
#include <cmath>
#include <cstddef>
template <std::ptrdiff_t> struct MInt {};
struct ConstructDestruct {
void (*m_destructor)(ConstructDestruct*);
template <typename T, std::ptrdiff_t offset>
ConstructDestruct(T* instance, MInt<offset>) :
m_destructor(+[](ConstructDestruct* self){
reinterpret_cast<T*>(reinterpret_cast<uintptr_t>(self) - offset)->destructor();
})
{
instance->constructor();
}
~ConstructDestruct() {
m_destructor(this);
}
};
#define offset_to(member)\
(MInt<offsetof(std::remove_reference<decltype(*this)>::type, member)>())
int main() {
struct {
double a { sqrt(4) };
int b { 42 };
ConstructDestruct cd { this, offset_to(cd) };
void constructor() {
std::cout << "constructed " << std::hex << (void*)this << std::endl;
}
void destructor() {
std::cout << "destructed " << std::hex << (void*)this << std::endl;
}
} instance1, instance2;
std::cout << "body" << std::endl;
}
Unfortunately, it doesn't seem possible to get rid of the function pointer from within ConstructDestruct. This isn't that bad, though, since its size needs to be non-zero. Whatever is stored immediately after the unnamed struct is likely to be aligned to a multiple of a function pointer size anyway, so there may be no overhead from the sizeof(ConstructDestruct) being larger than 1.
You can not declare a constructor or destructor for an unnamed class because the constructor and destructor names need to match the class name. In your example, the unnamed class is local. It has no linkage so neither mangled name is created.
If you are thinking of C++ names, then any class that has objects has to have a destructor whether you create it explicitly or not. So yes, the compiler knows how to assign a name. Whether that naming convention is any of your business, however, probably not.
Actually, you can create a structure or also a namespace without a name. You still need to have names somewhere because at the time you link all of that, the linker needs some kind of a name to make it all work, although in many cases it will be local names that are resolved immediately at compile time--by the assembler.
One way to know of the names assigned by the compiler is to look at the debug strings and see what corresponds to the different addresses that you're interested in. When you compile with -g then you should get all the necessary debug for your debugger to place your current at the right place with the right "names"... (I've see the namespaces without a name it says " namespace", I'm pretty sure structures use the same trick at a higher level.)

Overload ambiguity with std::function that takes an std::optional parameter

Here is a simple example program:
using fn_string = function<void(const string&)>;
using fn_optional_string = function<void(const optional<string>&)>;
void foo(fn_string) { cout << "string" << endl; }
void foo(fn_optional_string) { cout << "optional string" << endl; }
int main()
{
foo([&](const string&){ });
foo([&](const optional<string>&){ }); // <-- ambiguous
return 0;
}
It has 2 overloads for foo() -- one taking function with string parameter and another with optional<string>.
Why is 2nd call to foo() ambiguous?
Is there a simple way to fix it? Without casts?
UPDATE
The above was an overly simplified example of the following real world problem I am trying to solve, which is:
using delegate = variant<
function<void()>,
function<void(const string&)>,
function<void(const optional<string>&)>
>;
struct foo
{
void add_delegate(delegate fn) { fns.push_back(std::move(fn)); }
vector<delegate> fns;
};
int main()
{
foo bar;
bar.add_delegate([&](){ });
bar.add_delegate([&](const string&){ });
bar.add_delegate([&](const optional<string>&){ }); // ERROR
return 0;
}
The last call to add_delegate fails to compile, as it can't decide between function<void(const string&)> and function<void(const optional<string>&)>.
My understanding was that the issue had to do with overload resolution (hence my original example). What change should I make to add_delegate to permit it to accept all 3 versions of lambdas?
Complete example can be found on Coliru.
A lambda is not a std::function<>. A std::function<R(Args...)> is a type-erasure value type that can store any copyable object that is call-compatible with R(Args...).
In one case above, R is void (which for a std::function means "I don't care what it returns), and Args... is std::string. A callable object is call-compatible with this if you can call it with a std::string rvalue.
This is true of both std::optional<std::string> and std::string.
There is no special overload for "exact match" -- all that matters is, call compatible or not.
There are a few ways to handle this.
template<std::size_t N>
struct overload_order : overload_order<N-1> {};
template<>
struct overload_order<0> {};
namespace details {
void foo(overload_order<1>, fn_string) { cout << "string" << endl; }
void foo(overload_order<0>, fn_optional_string) { cout << "optional string" << endl; }
}
template<class F>
void foo(F&& f) {
foo( overload_order<!std::is_same<std::decay_t<F>, fn_optional_string>{}>{}, std::forward<F>(f) );
}
now we first try the fn_string one, and only if that fails do we try fn_optional_string, unless the argument is already a fn_optional_string, in which case we dispatch directly to that overload.
Declare the argument specifically as a fun_optional_string.
I don't know what to type to keep the software from complaining about a code only answer, so here's a poem:
There is an old hack from Milpitas...
His motto, "No bug can defeat us." ...
His resolve never lapses ...
As he fires those synapses ...
Fueled by doughnuts, cold rice, and fajitas. ...
#include <functional>
#include <iostream>
#include <optional>
#include <string>
using namespace std;
using fn_string = function<void(const string&)>;
using fn_optional_string = function<void(const optional<string>&)>;
void foo(fn_string) { cout << "string" << endl; }
void foo(fn_optional_string) { cout << "optional string" << endl; }
int main()
{
foo([&](const string&){ });
fn_optional_string g = [&](const optional<string>&) {};
foo(g); // <-- not ambiguous
return 0;
}
Well, it turned out to be simpler than I expected. All I had to do was add the following overload to struct foo:
void add_delegate(fn_opt_val fn) { add_delegate(delegate{ std::move(fn) }); }
Here is complete code on Coliru.

How to call method of wrapped object by unique_ptr?

I'm able to compile the following code where I pass a "callback" to an object (Table). What I'm trying to do now is inside Table, call the handle method defined in EventListener
#include <iostream>
#include <vector>
#include <memory>
class Table {
public:
struct Listener{
virtual void handle(int i) = 0;
};
std::vector<std::unique_ptr<Listener>> listeners_;
void add_listener(std::unique_ptr<Listener> l){
listeners_.push_back(std::move(l));
}
};
struct EventListener: public Table::Listener {
void handle(int e){
std::cout << "Something happened! " << e << " \n";
}
};
int main(int argc, char** argv)
{
Table table;
std::unique_ptr<EventListener> el;
table.add_listener(std::move(el));
return 0;
}
EDIT ****
This is what Im trying inside Table. It results in a segmentation fault:
for (auto t =0; t < (int)listeners_.size(); ++t) {
listeners_[t]->handle(event);
}
It doesn't work because you never created an object for it to be called on, just a pointer. The pointer inside the vector will be nullptr and therefore calling the function on it will crash. unique_ptr has absolutely nothing to do with this problem.
Half the problem is that Table cannot handle nullptr but doesn't check for it, and the other half the problem is that Table cannot handle nullptr but main passes one in anyway.
The iteration code is not the problem at all.
As mentioned in the answer by Puppy the line
std::unique_ptr<EventListener> el;
creates an empty std::unique_ptr. This causes the code for nivoking the
listeners to later dereference a nullptr.
A simple fix for your example is to create a listener and use that
when creating the unique_ptr:
struct EventListener: public Table::Listener {
void handle(int e){
std::cout << "Something happened! " << e << " \n";
}
};
// in main()
std::unique_ptr<NoOpListener> el{ new NoOpListener };
table.add_listener(std::move(el));
As mentioned in the comments your code should ensure that nullptr
isn't allowed. One way of doing this would be to add a check for
nullptr in add_listener and throw an exception or silently ignore
them. The first option is the better solution of the two as it
signals the caller that something is wrong.
But I don't see why you would store listeners in std::unique_ptrs.
The use for std::unique_ptr is for ownership. I do not see why the
observed instance should own the listeners. There is another alternative
that I think is better; use std::function<>() and pass it by value.
This disallows the use of nullptr and has the added bonus of accepting
not only function objects, but also normal functions and lambdas as shown
in the following code:
#include <iostream>
#include <vector>
#include <memory>
#include <functional>
class Table {
public:
std::vector<std::function<void(int)>> listeners_;
void add_listener(std::function<void(int)> l) {
listeners_.push_back(l);
}
void invoke_listeners(int event)
{
for(auto l : listeners_) {
l(event);
}
}
};
struct NoOpListener {
void operator() (int i) {
std::cout << "NoOpListener::operator()(" << i << ")" << std::endl;
}
};
void cb(int i) {
std::cout << "cb(" << i << ")" << std::endl;
}
int main(int argc, char** argv)
{
Table table;
table.add_listener(NoOpListener{});
table.add_listener(cb);
table.add_listener([](int i) { std::cout << "[lambda](" << i << ")" << std::endl; });
table.invoke_listeners(10);
return 0;
}
NOTE: As you can see I also used the C++11 ranged-for construct for iterating
over the listeners.

How to add constructors/destructors to an unnamed class?

Is there a way to declare a constructor or a destructor in an unnamed class? Consider the following
void f()
{
struct {
// some implementation
} inst1, inst2;
// f implementation - usage of instances
}
Follow up question : The instances are ofcourse constructed (and destroyed) as any stack based object. What gets called? Is it a mangled name automatically assigned by the compiler?
The simplest solution is to put a named struct instance as a member in the unnamed one, and put all of the functionality into the named instance. This is probably the only way that is compatible with C++98.
#include <iostream>
#include <cmath>
int main() {
struct {
struct S {
double a;
int b;
S() : a(sqrt(4)), b(42) { std::cout << "constructed" << std::endl; }
~S() { std::cout << "destructed" << std::endl; }
} s;
} instance1, instance2;
std::cout << "body" << std::endl;
}
Everything that follows requires C++11 value initialization support.
To avoid the nesting, the solution for the construction is easy. You should be using C++11 value initialization for all members. You can initialize them with the result of a lambda call, so you can really execute arbitrarily complex code during the initialization.
#include <iostream>
#include <cmath>
int main() {
struct {
double a { sqrt(4) };
int b { []{
std::cout << "constructed" << std::endl;
return 42; }()
};
} instance1, instance2;
}
You can of course shove all the "constructor" code to a separate member:
int b { [this]{ constructor(); return 42; }() };
void constructor() {
std::cout << "constructed" << std::endl;
}
This still doesn't read all that cleanly, and conflates the initialization of b with other things. You could move the constructor call to a helper class, at the cost of the empty class still taking up a bit of space within the unnamed struct (usually one byte if it's the last member).
#include <iostream>
#include <cmath>
struct Construct {
template <typename T> Construct(T* instance) {
instance->constructor();
}
};
int main() {
struct {
double a { sqrt(4) };
int b { 42 };
Construct c { this };
void constructor() {
std::cout << "constructed" << std::endl;
}
} instance1, instance2;
}
Since the instance of c will use some room, we might as well get explicit about it, and get rid of the helper. The below smells of a C++11 idiom, but is a bit verbose due to the return statement.
struct {
double a { sqrt(4) };
int b { 42 };
char constructor { [this]{
std::cout << "constructed" << std::endl;
return char(0);
}() };
}
To get the destructor, you need the helper to store both the pointer to an instance of the wrapped class, and a function pointer to a function that calls the destructor on the instance. Since we only have access to the unnamed struct's type in the helper's constructor, it's there that we have to generate the code that calls the destructor.
#include <iostream>
#include <cmath>
struct ConstructDestruct {
void * m_instance;
void (*m_destructor)(void*);
template <typename T> ConstructDestruct(T* instance) :
m_instance(instance),
m_destructor(+[](void* obj){ static_cast<T*>(obj)->destructor(); })
{
instance->constructor();
}
~ConstructDestruct() {
m_destructor(m_instance);
}
};
int main() {
struct {
double a { sqrt(4) };
int b { 42 };
ConstructDestruct cd { this };
void constructor() {
std::cout << "constructed" << std::endl;
}
void destructor() {
std::cout << "destructed" << std::endl;
}
} instance1, instance2;
std::cout << "body" << std::endl;
}
Now you're certainly complaining about the redundancy of the data stored in the ConstructDestruct instance. The location where the instance is stored is at a fixed offset from the head of the unnamed struct. You can obtain such offset and wrap it in a type (see here). Thus we can get rid of the instance pointer in the ConstructorDestructor:
#include <iostream>
#include <cmath>
#include <cstddef>
template <std::ptrdiff_t> struct MInt {};
struct ConstructDestruct {
void (*m_destructor)(ConstructDestruct*);
template <typename T, std::ptrdiff_t offset>
ConstructDestruct(T* instance, MInt<offset>) :
m_destructor(+[](ConstructDestruct* self){
reinterpret_cast<T*>(reinterpret_cast<uintptr_t>(self) - offset)->destructor();
})
{
instance->constructor();
}
~ConstructDestruct() {
m_destructor(this);
}
};
#define offset_to(member)\
(MInt<offsetof(std::remove_reference<decltype(*this)>::type, member)>())
int main() {
struct {
double a { sqrt(4) };
int b { 42 };
ConstructDestruct cd { this, offset_to(cd) };
void constructor() {
std::cout << "constructed " << std::hex << (void*)this << std::endl;
}
void destructor() {
std::cout << "destructed " << std::hex << (void*)this << std::endl;
}
} instance1, instance2;
std::cout << "body" << std::endl;
}
Unfortunately, it doesn't seem possible to get rid of the function pointer from within ConstructDestruct. This isn't that bad, though, since its size needs to be non-zero. Whatever is stored immediately after the unnamed struct is likely to be aligned to a multiple of a function pointer size anyway, so there may be no overhead from the sizeof(ConstructDestruct) being larger than 1.
You can not declare a constructor or destructor for an unnamed class because the constructor and destructor names need to match the class name. In your example, the unnamed class is local. It has no linkage so neither mangled name is created.
If you are thinking of C++ names, then any class that has objects has to have a destructor whether you create it explicitly or not. So yes, the compiler knows how to assign a name. Whether that naming convention is any of your business, however, probably not.
Actually, you can create a structure or also a namespace without a name. You still need to have names somewhere because at the time you link all of that, the linker needs some kind of a name to make it all work, although in many cases it will be local names that are resolved immediately at compile time--by the assembler.
One way to know of the names assigned by the compiler is to look at the debug strings and see what corresponds to the different addresses that you're interested in. When you compile with -g then you should get all the necessary debug for your debugger to place your current at the right place with the right "names"... (I've see the namespaces without a name it says " namespace", I'm pretty sure structures use the same trick at a higher level.)

Prevent reassignment of a reference?

Consider that in some library somewhere (which we have no access to change), we have a Counter class:
class Counter {
int count;
public:
Counter() : count(0) { }
void bump() { ++count; }
int getCount() const { return count; }
};
which, by its very nature, is mutable. If it's const, it's pretty worthless.
And in our code, we "use" that Counter. Badly.
#include <string>
#include <iostream>
#include <Counter.hpp>
using std::cout;
using std::endl;
void breakTheHellOutOfCounter(Counter &c) {
// This is OK
c.bump();
// Oh noes!
c = Counter();
}
int main() {
Counter c;
c.bump(); c.bump(); c.bump();
std::cout << "Count was " << c.getCount() << std::endl;
breakTheHellOutOfCounter(c);
std::cout << "Count is now " << c.getCount() << std::endl;
}
Note that breakTheHellOutOfCounter overwrites main's counter with a shiny new one, resetting the count. That's going to cause the caller some grief. (Imagine something a lot more harmful happening, and you'll see where I'm going here.)
I need to be able to bump c (and thus, I need it mutable), but I want breakTheHellOutOfCounter() to fail miserably due to trying to replace c. Is there a way I can change things (other than the Counter class) to make that happen?
(I'm aware that at the lowest levels, this is all but impossible to enforce. What I want is a way to make it hard to do accidentally.)
The cleanest solution I can see to this without modifying counter itself is something like:
#include <string>
#include <iostream>
#include <Counter.hpp>
template <typename T>
struct Unbreakable : public T {
Unbreakable<T>& operator=(const Unbreakable<T>&) = delete;
Unbreakable<T>& operator=(Unbreakable<T>&&) = delete;
template <typename ...Args>
Unbreakable(Args&& ...args) : T(std::forward<Args>(args)...) {}
};
using std::cout;
using std::endl;
void breakTheHellOutOfCounter(Unbreakable<Counter> &c) {
// this is ok
c.bump();
// oh noes!
c = Counter();
}
int main() {
Unbreakable<Counter> c;
c.bump(); c.bump(); c.bump();
std::cout << "Count was " << c.getCount() << std::endl;
breakTheHellOutOfCounter(c);
std::cout << "Count is now " << c.getCount() << std::endl;
}
Which correctly gives an error from your "oh noes" line. (Example uses C++11, but C++98 solution is similar)
That doesn't rule out usage like:
Counter& br = c;
br = Counter();
of course, but without modifying Counter itself I don't think that's avoidable.
The simplest way to do this is to remove the assignment operator from the Counter class. However, since you don't have the ability to change the Counter class, your only real option is to wrap the Counter class in a class with no assignment operator and use that instead.
As Michael Anderson said, you can wrap your counter object in a class that prevents assignment.
class CounterProxy {
Counter& counter;
CounterProxy & operator=(const CounterProxy&);
public:
CounterProxy(Counter& c) : counter(c) {}
void bump() { counter.bump(); }
int getCount() const { return counter.getCount(); }
};
void breakTheHellOutOfCounter(CounterProxy &c) {
// this is ok
c.bump();
// not oh noes!
c = CounterProxy(Counter());
}
int main() {
Counter c;
c.bump(); c.bump(); c.bump();
std::cout << "Count was " << c.getCount() << std::endl;
breakTheHellOutOfCounter(CounterProxy(c));
std::cout << "Count is now " << c.getCount() << std::endl;
}
You can use this method whenever you want to limit the operations that can be performed on an object.
EDIT: You're probably already aware of this and looking for a more elegant solution, but the code might help others.
By allowing bump via a mutable reference, you are giving the function access to mess with the object state. There is nothing special about assignment; it's just a function that mutates the object in some way. It could just as well be a function called CopyStateFromAnotherInstance() instead of operator =().
So the real problem is: How do you allow only certain functions but hide others? By using an interface:
class IBumpable
{
void bump() ...
};
class Counter : IBumpable
{
....
};
void functionThatCannotBreakCounter(IBumpable& counter) { ... }