C++ class changing what "*this" refers to - c++

I have a class "foo" which contains a member vector which holds elements of type "foo". This class has a method called "make", which creates "foo" objects and appends them to the vector. I also provide a method for navigating the "foo" object and it's "foo" vector elements called "get".
I would like to re-assign what "this" refers to on any "foo" object to point to a foo object within it's vector. I would like to give the code this functionality as a way to more efficiently navigate and keep track of foo objects without having to bind references to specific "foo" members. I am attempting to do this through a method called "setAsNode" which reassigns what "*this" is.
Here's some example code I mocked up which I believe gets my point across:
struct foo{
foo(const std::string &r):s(r){}
foo& make(const std::string &r){children.push_back(test(r)); children.back().originalNode = originalNode; return children.back();}
foo& setAsNode(){*originalNode = *this; return *originalNode;}
foo& get(int i){return children.at(i);}
private:
std::vector<foo> children;
std::string s = "someData";
foo *originalNode = this;
};
and a practical example of how this would function:
foo f1("test1");
f1.make("test2").make("test3");//pushes a new foo element back to the vector in f1 and pushes a new foo element back to it's vector
f1.get(0).get(0);//refers to the deepest nested object we just made above, cumbersome to type especially if the nesting is deep
f1.get(0).get(0).setAsNode();//this is where the error occurs, the desired effect is to make f1 the same object as denoted by f1.get(0).get(0);
foo& f2 = f1.get(0).get(0);//f2 would be equivalent to what I want the above code to reset f1 to be (or at least how I'd like it to act)
I realize I'm probably implementing some very bad programming practices such as using references instead of pointers for returning "foo" objects, but I honestly do not know how to correctly structure a program such as this, or how a best practice version of this program would look/work. Bonus points for whoever can show me the "right" way of doing things.
Back to the real question though: how would I make something like this, specifically the "setAsNode" method, actually work? Also, why doesn't the code in my example work? Note, it compiles fine, just crashes upon running.

The c++ way (and arguably only correct way) is to separate concerns.
A foo is not (or should not be) a foo-finder. It should do foo-things, not foo-navigation things.
Make a new class to act as the cursor or iterator of your foo.
Here is a slightly expanded version, in which the foo_cursor remembers its journey through the foo stack. Probably overkill for your problem but it demonstrates the principle of separating foo-navigation logic from foo-implementation logic.
The more you do this, the easier your programs will be to write, debug and maintain.
#include <utility>
#include <string>
#include <vector>
#include <stack>
#include <stdexcept>
#include <iostream>
struct foo{
foo(const std::string &r)
: children()
, s(r)
{}
foo& make(const std::string &r)
{
children.emplace_back(r);
return children.back();
}
foo& get(int i)
{
return children.at(i);
}
void print() const {
std::cout << s << std::endl;
}
private:
std::vector<foo> children;
std::string s = "someData";
};
struct foo_cursor
{
foo_cursor(foo& f)
: current_(std::addressof(f))
{}
foo_cursor& down(int i)
{
history_.push(current_);
current_ = std::addressof(current_->get(i));
return *this;
}
foo_cursor& up() {
if (history_.empty()) {
throw std::logic_error("went up too far");
}
else {
current_ = history_.top();
history_.pop();
}
return *this;
}
foo* operator->() const {
return current_;
}
private:
foo* current_;
std::stack<foo*> history_;
};
int main()
{
foo f("a");
f.make("b").make("c");
auto fc = foo_cursor(f);
fc.down(0).down(0)->print();
fc.up()->print();
fc.up()->print();
}
expected output:
c
b
a

In your example, the call to foo.get(0).get(0).setAsNode() will attempt to copy the value of foo.get(0).get(0) to foo. In the process, foo.children will be assigned a new value, causing the vector to clear it's previous elements leading to the destruction of foo.get(0).get(0). This means that this has been destroyed and that the pointer can't be used. However, this is happening during an assignment operation which us currently using this. To solve this, you must insure that the value being copied persists long enough to be copied. The intuitive solution might be to copy the value to assign before assigning it.
foo& setAsNode() {
auto this_copy = *this;
*originalNode = std::move(this_copy);
return *originalNode;
}
This will work, you still have to be careful never to use this after the assignment to *originalNode. Another solution would be to take control of originalNode's children vector before preforming the assignment. In this version this remains valid until the method returns but if the following assignment throws an exception your tree will be left in an invalid state.
foo& setAsNode() {
auto original_vect = std::move(originalNode->children);
*originalNode = *this;
return *originalNode;
}
All in all, I would be wary of a design that calls for objects to commit suicide. It implies object controls their own ownership or that ownership responsibilities are otherwise cyclical.

Related

Ways to imply return values are not meant to be stored

We can use nodiscard attribute to imply that the return value of a function should not be discarded. Are there any attribute (or other ways) to imply some opposite semantics: the return value of the function should only be used temporarily (by "temporary" I mean, not to assign to any variable except local ones)?
As the purpose may not be immediately clear, consider I have a class FooHolder that holds resources Foo; calling FooHolder::getFoo() returns the Foo it is currently holding:
#include <memory>
class Foo {
public:
Foo& bar() { /* do something */ return *this; }
const Foo& far() const { /* do something else */ return *this; }
};
class FooHolder {
private:
std::shared_ptr<Foo> _foo { nullptr };
public:
FooHolder(): _foo(std::make_shared<Foo>()) {}
Foo& getFoo() { return *_foo; }
const Foo& getFoo() const { return *_foo; }
};
And we may use it in many ways:
// Others may try storing some status:
Foo* g_foo = nullptr;
int main() {
FooHolder foo_holder {};
// I want to support this:
foo_holder.getFoo().bar().far() /* chained calls... */ ;
// Also, maybe this:
auto& local_foo = foo_holder.getFoo();
local_foo.bar();
local_foo.far();
// But not this, because the Foo instance that FooHolder holds may perish:
static Foo& static_foo = foo_holder.getFoo();
// Nor this:
g_foo = &local_foo;
return 0;
}
So are there ways to prevent (or at least warn about) storing the return value of FooHolder::getFoo()? Or, is it bad practice to return resources by reference?
[...] is it bad practice to return resources by reference?
It depends. There are many examples of methods that return non-const references and they are all fine. For example consider standard container element accessors. However, they are not meant for encapsulation. std::vector::operator[] is not meant to hide the elements from the caller, it is meant to provide direct access to it. Returning a non-const reference is not encapsulation! It is the opposite. Note that std::vector even grants you access to its data(). Thats not encapsulation either, it is relying on the user to not delete[] some_vect.data() or do other wrong stuff that would break the vector.
You want FooHolder to encapsulate the contained Foo.
This is opposing requirements.
You have basically two choices: A) The caller knows what they are doing. They read documentation. They know that they are not supposed to use FooHolder::getFoo in the wrong way. B) use proper encapsulation: Never give the caller direct access to a non-const Foo:
class FooHolder {
private:
std::shared_ptr<Foo> _foo { nullptr };
public:
FooHolder(): _foo(std::make_shared<Foo>()) {}
// nope // Foo& getFoo() { return *_foo; }
// maybe // const Foo& getFoo() const { return *_foo; }
FooHolder& bar() {
_foo->bar();
return *this;
}
// ..same for far() ...
};
Note that A) is a viable solution. Consider that also things like std::shared_ptr can be used terribly wrong. Users are expected to know how to use it right. The difference is that std::shared_ptr is a standard type with plenty of documentation. Hence, you should think twice if this is the way to go.
So are there ways to prevent (or at least warn about) storing the return value of FooHolder::getFoo()?
No. Once you returned a non-const reference all bets are off. FooHolder is no longer in control of what the caller can do with that reference. You can prevent copies or moves but you cannot prevent holding on to a reference.

Validity of pointer returned by operator->

I'm implementing a two-dimensional array container (like boost::multi_array<T,2>, mostly for practice). In order to use double-index notation (a[i][j]), I introduced a proxy class row_view (and const_row_view but I'm not concerned about constness here) which keeps a pointer to the beginning and end of the row.
I would also like to be able to iterate over rows and over elements within a row separately:
matrix<double> m;
// fill m
for (row_view row : m) {
for (double& elem : row) {
// do something with elem
}
}
Now, the matrix<T>::iterator class (which is meant to iterate over rows) keeps a private row_view rv; internally to keep track of the row the iterator is pointing to. Naturally, iterator also implements dereferenciation functions:
for operator*(), one would usually want to return a reference. Instead, here the right thing to do seems to return a row_view by value (i.e. return a copy of the private row_view). This ensures that when the iterator is advanced, the row_view still points to the previous row. (In a way, row_view acts like a reference would).
for operator->(), I'm not so sure. I see two options:
Return a pointer to the private row_view of the iterator:
row_view* operator->() const { return &rv; }
Return a pointer to a new row_view (a copy of the private one). Because of storage lifetime, that would have to be allocated on the heap. In order to ensure clean-up, I'd wrap it in a unique_ptr:
std::unique_ptr<row_view> operator->() const {
return std::unique_ptr<row_view>(new row_view(rv));
}
Obviously, 2 is more correct. If the iterator is advanced after operator-> is called, the row_view that is pointed to in 1 will change. However, the only way I can think of where this would matter, is if the operator-> was called by its full name and the returned pointer was bound:
matrix<double>::iterator it = m.begin();
row_view* row_ptr = it.operator->();
// row_ptr points to view to first row
++it;
// in version 1: row_ptr points to second row (unintended)
// in version 2: row_ptr still points to first row (intended)
However, this is not how you'd typically use operator->. In such a use case, you'd probably call operator* and keep a reference to the first row. Usually, one would immediately use the pointer to call a member function of row_view or access a member, e.g. it->sum().
My question now is this: Given that the -> syntax suggests immediate use, is the validity of the pointer returned by operator-> considered to be limited to that situation, or would a safe implementation account for the above "abuse"?
Obviously, solution 2 is way more expensive, as it requires heap-allocation. This is of course very much undesirable, as dereferenciation is quite a common task and there is no real need for it: using operator* instead avoids these problems as it returns a stack-allocated copy of the row_view.
As you know, operator-> is applied recursively on the functions return type until a raw pointer is encountered. The only exception is when it's called by name like in your code sample.
You can use that to your advantage and return a custom proxy object. To avoid the scenario in your last code snippet, this object needs to satisfy several requirements:
Its type name should be private to the matrix<>::iterator, so outside code could not refer to it.
Its construction/copy/assignment should be private. matrix<>::iterator will have access to those by virtue of being a friend.
An implementation will look somewhat like this:
template <...>
class matrix<...>::iterator {
private:
class row_proxy {
row_view *rv_;
friend class iterator;
row_proxy(row_view *rv) : rv_(rv) {}
row_proxy(row_proxy const&) = default;
row_proxy& operator=(row_proxy const&) = default;
public:
row_view* operator->() { return rv_; }
};
public:
row_proxy operator->() {
row_proxy ret(/*some row view*/);
return ret;
}
};
The implementation of operator-> returns a named object to avoid any loopholes due to guaranteed copy elision in C++17. Code that use the operator inline (it->mem) will work as before. However, any attempt to call operator->() by name without discarding the return value, will not compile.
Live Example
struct data {
int a;
int b;
} stat;
class iterator {
private:
class proxy {
data *d_;
friend class iterator;
proxy(data *d) : d_(d) {}
proxy(proxy const&) = default;
proxy& operator=(proxy const&) = default;
public:
data* operator->() { return d_; }
};
public:
proxy operator->() {
proxy ret(&stat);
return ret;
}
};
int main()
{
iterator i;
i->a = 3;
// All the following will not compile
// iterator::proxy p = i.operator->();
// auto p = i.operator->();
// auto p{i.operator->()};
}
Upon further review of my suggested solution, I realized that it's not quite as fool-proof as I thought. One cannot create an object of the proxy class outside the scope of iterator, but one can still bind a reference to it:
auto &&r = i.operator->();
auto *d = r.operator->();
Thus allowing to apply operator->() again.
The immediate solution is to qualify the operator of the proxy object, and make it applicable only to rvalues. Like so for my live example:
data* operator->() && { return d_; }
This will cause the two lines above to emit an error again, while the proper use of the iterator still works. Unfortunately, this still doesn't protect the API from abuse, due to the availability of casting, mainly:
auto &&r = i.operator->();
auto *d = std::move(r).operator->();
Which is a death blow to the whole endeavor. There is no preventing this.
So in conclusion, there is no protection from a direction call to operator-> on the iterator object. At the most, we can only make the API really hard to use incorrectly, while the correct usage remains easy.
If creation of row_view copies is expansive, this may be good enough. But that is for you to consider.
Another point for consideration, which I haven't touched on in this answer, is that the proxy could be used to implement copy on write. But that class could be just as vulnerable as the proxy in my answer, unless great care is taken and fairly conservative design is used.

Hint compiler to return a reference making 'auto' behave

(possibly related to How to implement a C++ method that creates a new object, and returns a reference to it which is about something different, but incidentially contains almost exactly the same code)
I would like to return a reference to a static local from a static function. I can get it to work, of course, but it's less pretty than I'd like.
Can this be improved?
The background
I have a couple of classes which don't do much except acquire or initialize a resource in a well-defined manner and reliably, and release it. They don't even need to know an awful lot about the resource themselves, but the user might still want to query some info in some way.
That's of course trivially done:
struct foo { foo() { /* acquire */ } ~foo(){ /* release */ } };
int main()
{
foo foo_object;
// do stuff
}
Trivial. Alternatively, this would work fine as well:
#include <scopeguard.h>
int main
{
auto g = make_guard([](){ /* blah */}, [](){ /* un-blah */ });
}
Except now, querying stuff is a bit harder, and it's less pretty than I like. If you prefer Stroustrup rather than Alexandrescu, you can include GSL instead and use some concoction involving final_act. Whatever.
Ideally, I would like to write something like:
int main()
{
auto blah = foo::init();
}
Where you get back a reference to an object which you can query if you wish to do that. Or ignore it, or whatever. My immediate thought was: Easy, that's just Meyer's Singleton in disguise. Thus:
struct foo
{
//...
static bar& init() { static bar b; return b; }
};
That's it! Dead simple, and perfect. The foo is created when you call init, you get back a bar that you can query for stats, and it's a reference so you are not the owner, and the foo automatically cleans up at the end.
Except...
The issue
Of course it couldn't be so easy, and anyone who has ever used range-based for with auto knows that you have to write auto& if you don't want surprise copies. But alas, auto alone looked so perfectly innocent that I didn't think of it. Also, I'm explicitly returning a reference, so what can auto possibly capture but a reference!
Result: A copy is made (from what? presumably from the returned reference?) which of course has a scoped lifetime. Default copy constructor is invoked (harmless, does nothing), eventually the copy goes out of scope, and contexts are released mid-operation, stuff stops working. At program end, the destructor is called again. Kaboooom. Huh, how did that happen.
The obvious (well, not so obvious in the first second!) solution is to write:
auto& blah = foo::init();
This works, and works fine. Problem solved, except... except it's not pretty and people might accidentially just do it wrong like I did. Can't we do without needing an extra ampersand?
It would probably also work to return a shared_ptr, but that would involve needless dynamic memory allocation and what's worse, it would be "wrong" in my perception. You don't share ownership, you are merely allowed to look at something that someone else owns. A raw pointer? Correct for semantics, but... ugh.
By deleting the copy constructor, I can prevent innocent users from running into the forgot-& trap (this will then cause a compiler error).
That is however still less pretty than I would like. There must be a way of communicating "This return value is to be taken as reference" to the compiler? Something like return std::as_reference(b);?
I had thought about some con trick involving "moving" the object without really moving it, but not only will the compiler almost certainly not let you move a static local at all, but if you manage to do it, you have either changed ownership, or with a "fake move" move-constructor again call the destructor twice. So that's no solution.
Is there a better, prettier way, or do I just have to live with writing auto&?
Something like return std::as_reference(b);?
You mean like std::ref? This returns a std::reference_wrapper<T> of the value you provide.
static std::reference_wrapper<bar> init() { static bar b; return std::ref(b); }
Of course, auto will deduce the returned type to reference_wrapper<T> rather than T&. And while reference_wrapper<T> has an implicit operatorT&, that doesn't mean the user can use it exactly like a reference. To access members, they have to use -> or .get().
That all being said however, I believe your thinking is wrong-headed. The reason is that auto and auto& are something that every C++ programmer needs to learn how to deal with. People aren't going to make their iterator types return reference_wrappers instead of T&. People don't generally use reference_wrapper in that way at all.
So even if you wrap all of your interfaces like this, the user still has to eventually know when to use auto&. So really, the user hasn't gained any safety, outside of your particular APIs.
Forcing the user to capture by reference is a three-step process.
First, make the returned thing non-copyable:
struct bar {
bar() = default;
bar(bar const&) = delete;
bar& operator=(bar const&) = delete;
};
then create a little passthrough function that delivers references reliably:
namespace notstd
{
template<class T>
decltype(auto) as_reference(T& t) { return t; }
}
Then write your static init() function, returning decltype(auto):
static decltype(auto) init()
{
static bar b;
return notstd::as_reference(b);
}
Full demo:
namespace notstd
{
template<class T>
decltype(auto) as_reference(T& t) { return t; }
}
struct bar {
bar() = default;
bar(bar const&) = delete;
bar& operator=(bar const&) = delete;
};
struct foo
{
//...
static decltype(auto) init()
{
static bar b;
return notstd::as_reference(b);
}
};
int main()
{
auto& b = foo::init();
// won't compile == safe
// auto b2 = foo::init();
}
Skypjack noted correctly that init() could be written just as correctly without notstd::as_reference():
static decltype(auto) init()
{
static bar b;
return (b);
}
The parentheses around the return (b) force the compiler to return a reference.
My problem with this approach is that c++ developers are often surprised to learn this, so it could be easily missed by a less experienced code maintainer.
My feeling is that return notstd::as_reference(b); explicitly expresses intent to code maintainers, much as std::move() does.
The best, most idiomatic, readable, unsurprising thing to do would be to =delete the copy constructor and copy assignment operator and just return a reference like everybody else.
But, seeing as you brought up smart pointers...
It would probably also work to return a shared_ptr, but that would involve needless dynamic memory allocation and what's worse, it would be "wrong" in my perception. You don't share ownership, you are merely allowed to look at something that someone else owns. A raw pointer? Correct for semantics, but... ugh.
A raw pointer would be perfectly acceptable here. If you don't like that, you have a number of options following the "pointers" train of thought.
You could use a shared_ptr without dynamic memory, with a custom deleter:
struct foo {
static shared_ptr<bar> init() { static bar b; return { &b, []()noexcept{} }; }
}
Although the caller doesn't "share" ownership, it's not clear what ownership even means when the deleter is a no-op.
You could use a weak_ptr holding a reference to the object managed by the shared_ptr:
struct foo {
static weak_ptr<bar> init() { static bar b; return { &b, []()noexcept{} }; }
}
But considering the shared_ptr destructor is a no-op, this isn't really any different from the previous example, and it just imposes on the user an unnecessary call to .lock().
You could use a unique_ptr without dynamic memory, with a custom deleter:
struct noop_deleter { void operator()() const noexcept {} };
template <typename T> using singleton_ptr = std::unique_ptr<T, noop_deleter>;
struct foo {
static singleton_ptr<bar> init() { static bar b; return { &b, {} }; }
}
This has the benefit of not needing to manage a meaningless reference count, but again the semantic meaning is not a perfect fit: the caller does not assume unique ownership, whatever ownership really means.
In library fundamentals TS v2 you can use observer_ptr, which is just a raw pointer that expresses the intent to be non-owning:
struct foo {
static auto init() { static bar b; return experimental::observer_ptr{&b}; }
}
If you don't like any of these options, you can of course define your own smart pointer type.
In a future standard you may be able to define a "smart reference" that works like reference_wrapper without the .get() by utilising overloaded operator..
If you want to use singleton, use it correctly
class Singleton
{
public:
static Singleton& getInstance() {
static Singleton instance;
return instance;
}
Singleton(const Singleton&) = delete;
Singleton& operator =(const Singleton&) = delete;
private:
Singleton() { /*acquire*/ }
~Singleton() { /*Release*/ }
};
So you cannot create copy
auto instance = Singleton::getInstance(); // Fail
whereras you may use instance.
auto& instance = Singleton::getInstance(); // ok.
But if you want scoped RAII instead of singleton, you may do
struct Foo {
Foo() { std::cout << "acquire\n"; }
~Foo(){ std::cout << "release\n"; }
Foo(const Foo&) = delete;
Foo& operator =(const Foo&) = delete;
static Foo init() { return {}; }
};
With the usage
auto&& foo = Foo::init(); // ok.
And copy is still forbidden
auto foo = Foo::init(); // Fail.
Someone would make a typo in one day and then we may or may not notice it in so many code, although we all know we should use auto& instead of auto.
The most convenient but very dangerous solution is using a derived class, as it breaks the strict-aliasing rule.
struct foo
{
private:
//Make these ref wrapper private so that user can't use it directly.
template<class T>
class Pref : public T {
private:
//Make ctor private so that we can issue a compiler error when
//someone typo an auto.
Pref(const Pref&) = default;
Pref(Pref&&) = default;
Pref& operator=(const Pref&) = default;
Pref& operator=(Pref&&) = default;
};
public:
static Pref<bar>& init() {
static bar b;
return static_cast<Pref<bar>&>(b);
}
///....use Pref<bar>& as well as bar&.
};

deep copy of object which hold references to other objects

I have a "sum" class which holds two references to existing ints (say). I want to create a "copy" method which deep copies the ints. I thought I would never have to manually delete objects in my code, thanks to smart pointers, but I had to in this solution. Moreover, it is too complicated for a so trivial task (which I need to repeat for several classes). Is there a more straightforward solution?
Note: I don't want to add a bool member (flag) to each objects to determine if the ints must be deleted (in my case, it's not a better overhead than the std::set check overhead in the destructor)
#include <set>
struct sum {
const int &a, &b;
static std::set<const int*> allocated_ints;
sum(const int& a, const int&b): a(a), b(b) {}
sum copy() const {
sum res(*new const int(a), *new const int(b));
allocated_ints.insert(&res.a);
allocated_ints.insert(&res.b);
return res;
}
~sum() {
if (allocated_ints.count(&this->a)) {
delete &this->a;
delete &this->b;
allocated_ints.erase(&this->a);
allocated_ints.erase(&this->b);
}
}
};
std::set<const int*> sum::allocated_ints;
What's the point of a "deep" copy of constants? The constants are going to have the same value no matter what! So just copy (i.e. alias) the const-references:
struct Foo
{
const int & n;
Foo(const int & m) : n(m) { }
Foo(const Foo & rhs) : n(rhs.n) { }
Foo copy() const { Foo f(*this); /* ... */ return f; }
// ...
};
If you're worried about dangling references when returning a copy from a function with a reference to a local variable, then don't make the class have const references, but copies. That way you naturally give your class the copy semantics that you seem to be after anyway.
If you were thinking that you could make a hybrid which is either non-owning or becomes owning depending on how you use it, then I'd say that's bad design that you should avoid. Decide whether your class has ownership over the data or not and then roll with it.
I think you're mixing-up two incompatible concepts.
If you initialize by reference you should refer to existing object whose lifetime is already defined and should be longer than your objects.
If you want to create a copy of your object, since it refers to something, your copy will also refer to that something.
If you want to own yourself dynamic supplied objects, you should use pointers for that, and acquire them as pointers (and delete them on destruction). A copy can then deep-create copies of the pointed objects (or can share them using reference counting or shared_ptr).
You are -in fact- making up a mixing of the two things, resulting in possible problems: think to:
int main()
{
const int x=5; //whatever it is
Foo foo(x);
// ...
} //danger here! ~Foo() will delete x
The references are not deep copied, because they point to an object. Therefore, your code fixed should look like this :
struct sum {
const int &a, &b;
sum(const int& a, const int&b): a(a), b(b) {}
sum copy() const {
sum res(a,b);
return res;
}
~sum() {
}
};

Non-owning holder with assignment semantics

I have a class that should hold a reference to some data, without owning that data (i.e. the actual data is guaranteed not to go out of scope). In particular, the class cannot make a copy – the data is easily several gigabytes in size.
Now, the usual implementation (I assume) is to have a reference to the data:
struct holder_ref {
type const& value;
holder_ref(type const& value) : value(value) { }
};
(Please note that the constness has absolutely no bearing on the problem).
Now, I absolutely need this class to be assignable (i.e. have a working operator =). I thought this was a fairly common problem but I can’t remember how (if ever) I’ve solved it before.
The problem is that a reference cannot be assigned and there’s simply no way around this. The only solution I’ve come up with uses placement new in place of the assignment operator:
// x = other_x; gets replaced with:
x.~T();
new (&x) T(other_x);
Now, this works and is standard compliant. But it sure is ugly. No – inacceptable.
So I’m searching for alternatives. One idea is to use pointers, but I’m unsure whether my constructor is actually guaranteed to work (and passing a pointer is impossible due to the interface I have to adhere to):
struct holder_ptr {
type const* value;
// Is this legal?
holder_ptr(type const& value = 0) : value(&value) { }
};
But I’d rather use a reference, if at all possible. Only – how to implement the assignment operator?
struct holder_ref {
type const& value;
holder_ref(type const& value = 0) : value(value) { }
holder_ref& operator =(holder_ref const& other) {
// Now what?!
return *this;
}
};
As a test case, consider the following code:
int main() {
int const TEST1 = 23;
int const TEST2 = 13;
int const TEST3 = 42;
std::vector<holder_ptr> hptr(1);
std::vector<holder_ref> href(2);
// Variant 1. Pointer.
hptr[0] = holder_ptr(TEST1);
// Variant 2. Placement new.
href[0].~holder_ref();
new (&href[0]) holder_ref(TEST2);
// Variant 3. ???
href[1] = holder_ref(TEST3);
assert(*hptr[0].value == TEST1); // Works (?)
assert(href[0].value == TEST2); // Works
assert(href[1].value == TEST3); // BOOM!
}
(Also, just to make this clear – the type we’re talking about is non-POD and I need a standard compliant solution.)
I don't see anything wrong with using a holder_ptr. It can be implemented something like so:
struct bad_holder : std::exception { };
struct holder_ptr {
holder_ptr() : value(0) { }
holder_ptr(type const& value) : value(&value) { }
type const& get() {
if (value == 0) throw bad_holder();
return *value;
}
private:
type const* value;
};
So long as you always assign to the pointer from a reference, you know that you have a valid object (that, or you ended up with a "null reference" previously, in which case you have other, bigger problems since you'll already have invoked undefined behavior).
With this solution, the interface is implemented entirely in terms of references, but under the hood a pointer is used so that the type is assignable. The use of references in the interface ensures there are none of the concerns that come with using pointers (namely, you never have to worry whether the pointer is null).
Edit: I've updated the example to allow for the holder to be default constructible.
I'd use the pointer holder. But if you are dead set against that, how about hiding your placement new operator=:
holder_ref& operator =(holder_ref const& other) {
new (this) holder_ref(other);
return *this;
}
Is a TR1 weak_ptr standard compliant enough?