Initializing member references to dummy member variables


K

K. Frank

Hello Group!

Suppose I have a class that has a member reference
variable, and sometimes I want to initialize it,
and sometimes I don't. How much sense does it make
to initialize it to a dummy member variable (to shut
the compiler up)?

As a concrete example, let's say I have a class that
is constructed with a value of or reference to some kind
of handle, but the handle might be a number or it might
be a string:

struct Handle {
Handle (int iHandle) :
iHandle_(iHandle),
sHandle_(dummy),
useIHandle_(true)
{}
Handle (const std::string& sHandle) :
iHandle_(0),
sHandle_(sHandle),
useIHandle_(false)
{}
const int iHandle_;
const std::string& sHandle_;
bool useIHandle_;
std::string dummy_;
}

The point is that the member reference variable sHandle_
is supposed to be initialized (in the constructors'
initialization lists), whether or not it's actually
going to be used. The constructor that takes an int
argument doesn't have any std::strings floating
around with which to initialize sHandle_, hence the
introduction of the member variable dummy_.

Is this a reasonable approach? Is there an established
idiom for doing this kind of thing?


Thanks for any wisdom.


K. Frank
 
Ad

Advertisements

L

Luca Risolia

K. Frank said:
struct Handle {
Handle (int iHandle) :
iHandle_(iHandle),
sHandle_(dummy),
useIHandle_(true)
{}
Handle (const std::string& sHandle) :
iHandle_(0),
sHandle_(sHandle),
useIHandle_(false)
{}
const int iHandle_;
const std::string& sHandle_;
bool useIHandle_;
std::string dummy_;
}

The point is that the member reference variable sHandle_
is supposed to be initialized (in the constructors'
initialization lists), whether or not it's actually
going to be used. The constructor that takes an int
argument doesn't have any std::strings floating
around with which to initialize sHandle_, hence the
introduction of the member variable dummy_.

Is this a reasonable approach? Is there an established
idiom for doing this kind of thing?

Use boost::eek:ptional or wait for std::eek:ptional :

#include <iostream>
#include <string>
#include <boost/optional.hpp>

using boost::eek:ptional; // there will be std::eek:ptional in C++1y

struct Handle {
Handle(int iHandle) : iHandle_(iHandle) { }
Handle(const std::string& sHandle) : iHandle_(0), sHandle_(sHandle) { }
const int iHandle_;
optional<const std::string&> sHandle_;
};

int main() {
std::string s = "Hello World!";
Handle h1(2);
Handle h2(s);
// ...
if (h2.sHandle_)
std::cout << h2.sHandle_.get() << '\n';
}
 
I

Ike Naar

Suppose I have a class that has a member reference
variable, and sometimes I want to initialize it,
and sometimes I don't. How much sense does it make
to initialize it to a dummy member variable (to shut
the compiler up)?

As a concrete example, let's say I have a class that
is constructed with a value of or reference to some kind
of handle, but the handle might be a number or it might
be a string:

struct Handle {
Handle (int iHandle) :
iHandle_(iHandle),
sHandle_(dummy),

Assuming you mean dummy_ instead of dummy.
useIHandle_(true)
{}
Handle (const std::string& sHandle) :
iHandle_(0),
sHandle_(sHandle),
useIHandle_(false)
{}
const int iHandle_;
const std::string& sHandle_;
bool useIHandle_;
std::string dummy_;
}

The point is that the member reference variable sHandle_
is supposed to be initialized (in the constructors'
initialization lists), whether or not it's actually
going to be used. The constructor that takes an int
argument doesn't have any std::strings floating
around with which to initialize sHandle_, hence the
introduction of the member variable dummy_.

Is this a reasonable approach? Is there an established
idiom for doing this kind of thing?

Why is useIHandle nonconst?
What is the reason for making sHandle_ a const reference
instead of a member variable?
In that case you wouldn't need the dummy_ member:

struct Handle
{
Handle(int iHandle)
: iHandle_(iHandle), useIHandle_(true)
{}
Handle(const std::string& sHandle)
: iHandle_(0), sHandle_(sHandle), useIHandle_(false)
{}
const int iHandle_;
const std::string sHandle_;
const bool useIHandle_;
}
 
N

Norman J. Goldstein

Hello K, Frank!

Hello Group!

Suppose I have a class that has a member reference
variable, and sometimes I want to initialize it,
and sometimes I don't. How much sense does it make
to initialize it to a dummy member variable (to shut
the compiler up)?

As a concrete example, let's say I have a class that
is constructed with a value of or reference to some kind
of handle, but the handle might be a number or it might
be a string:

struct Handle {
Handle (int iHandle) :
iHandle_(iHandle),
sHandle_(dummy),
useIHandle_(true)
{}
Handle (const std::string& sHandle) :
iHandle_(0),
sHandle_(sHandle),
useIHandle_(false)
{}
const int iHandle_;
const std::string& sHandle_;
bool useIHandle_;
std::string dummy_;
}

The point is that the member reference variable sHandle_
is supposed to be initialized (in the constructors'
initialization lists), whether or not it's actually
going to be used. The constructor that takes an int
argument doesn't have any std::strings floating
around with which to initialize sHandle_, hence the
introduction of the member variable dummy_.

Is this a reasonable approach? Is there an established
idiom for doing this kind of thing?

If you use a pointer instead of a reference, you can set it
to NULL initially, and then to something else, later, if the
opportunity arises. You cannot reassign the reference to
something else, later. My opinion is that if you are using a
reference, it should always be initialized with a useful
rvalue.
 
J

James Kanze

Suppose I have a class that has a member reference
variable, and sometimes I want to initialize it,
and sometimes I don't. How much sense does it make
to initialize it to a dummy member variable (to shut
the compiler up)?
As a concrete example, let's say I have a class that
is constructed with a value of or reference to some kind
of handle, but the handle might be a number or it might
be a string:
struct Handle {
Handle (int iHandle) :
iHandle_(iHandle),
sHandle_(dummy),
useIHandle_(true)
{}
Handle (const std::string& sHandle) :
iHandle_(0),
sHandle_(sHandle),
useIHandle_(false)
{}
const int iHandle_;
const std::string& sHandle_;
bool useIHandle_;
std::string dummy_;
}
The point is that the member reference variable sHandle_
is supposed to be initialized (in the constructors'
initialization lists), whether or not it's actually
going to be used. The constructor that takes an int
argument doesn't have any std::strings floating
around with which to initialize sHandle_, hence the
introduction of the member variable dummy_.
Is this a reasonable approach? Is there an established
idiom for doing this kind of thing?

Why is the variable a reference? The class std::string
represents values, and in most cases, should be a value, and not
a reference. For starters, references can dangle.
 
K

K. Frank

Hello All!

First off, let me thank Luca for directing me to
boost::eek:ptional / std::eek:ptional. That looks like
the semantics I am looking for.

Let me answer several questions by several respondents
in this one post, below.


Answers to questions raised in previous posts:
Use boost::eek:ptional or wait for std::eek:ptional :

Indeed. This looks like the best answer, given
the specific question I asked.

It's interesting to note that the c++14 proposal
for std::eek:ptional leaves open whether optional
references should be supported:

http://isocpp.org/files/papers/N3672.html#optional_ref

sHandle_(dummy),
Assuming you mean dummy_ instead of dummy.

Yes, "sHandle_(dummy)" should have read "sHandle_(dummy_)".
Why is useIHandle nonconst?

No particular reason. useIHandle_ was introduced really
just for expository purposes.
What is the reason for making sHandle_ a const reference
instead of a member variable?
In that case you wouldn't need the dummy_ member:

There are a couple of possible reasons. (Again, this
is just an example.)

One is to stick with style that says to pass class
objects by const reference rather than value. (A
style that I believe James promotes.)

If the value being passed in were bulkier than a
(short) string, passing by (const) reference could
be significantly cheaper. (But if the default value
of the class were comparably bulky, the use of the
dummy variable could vitiate any savings.)

And (in the analogous non-const case) one would
want a reference if the original passed-in argument
were to be modified.
If you use a pointer instead of a reference, you can set it
to NULL initially, and then to something else, later, if the
opportunity arises. You cannot reassign the reference to
something else, later. My opinion is that if you are using a
reference, it should always be initialized with a useful
rvalue.

Yes, using a pointer, with the possibility of initializing
it to a nullptr value, seems very reasonable and natural.
Are there any reasons to prefer boost/std::eek:ptional over
the tried-and-true pointer approach? Are there any benefits
(if only syntactic sugar) to the caller of the constructor?
Why is the variable a reference? The class std::string
represents values, and in most cases, should be a value, and not
a reference. For starters, references can dangle.

Ach, James, you of all people! Just the other day you
were expounding upon the virtues of passing strings by
const reference:

Speed of passing a string by value vs. const reference
https://groups.google.com/forum/#!topic/comp.lang.c++/0UuKuYp0fqY

Kidding aside, obviously for a (short) string, passing
by value would work just fine and would avoid the
uninitialized reference issue. But in use cases where
the object being passed in was too expensive to copy or
needed to be modified, pass-by-value wouldn't be appropriate.

It seems to me that for the use case I have in mind,
using a pointer or an optional would be the way to go.

Leaving aside the fact that optional is not yet standard,
is there any reason to prefer one over the other?
Maybe using the old-fashioned pointer solution is the
cleanest way to go here.


Thanks for everyone's thoughts.


K. Frank
 
Ad

Advertisements

L

Luca Risolia

K. Frank said:
It seems to me that for the use case I have in mind,
using a pointer or an optional would be the way to go.

Leaving aside the fact that optional is not yet standard,
is there any reason to prefer one over the other?
Maybe using the old-fashioned pointer solution is the
cleanest way to go here.

optional is safer. Also, in general, another reason is the following (taken
from the boost::eek:ptional documentation):

"However, pointers are good to refer to optional objects, but not particularly
good to handle the optional objects in all other respects, such as
initializing or moving/copying them. The problem resides in the shallow-copy
of pointer semantics: if you need to effectively move or copy the object,
pointers alone are not enough. The problem is that copies of pointers do not
imply copies of pointees. For example, as was discussed in the motivation,
pointers alone cannot be used to return optional objects from a function
because the object must move outside from the function and into the caller's
context.

A solution to the shallow-copy problem that is often used is to resort to
dynamic allocation and use a smart pointer to automatically handle the details
of this. For example, if a function is to optionally return an object X, it
can use shared_ptr<X> as the return value. However, this requires dynamic
allocation of X. If X is a built-in or small POD, this technique is very poor
in terms of required resources. Optional objects are essentially values so it
is very convenient to be able to use automatic storage and deep-copy semantics
to manipulate optional values just as we do with ordinary values. Pointers do
not have this semantics, so are inappropriate for the initialization and
transport of optional values, yet are quite convenient for handling the access
to the possible undefined value because of the idiomatic aid present in the
OptionalPointee concept incarnated by pointers. "
 
Ö

Öö Tiib

Leaving aside the fact that optional is not yet standard,
is there any reason to prefer one over the other?
Maybe using the old-fashioned pointer solution is the
cleanest way to go here.

Reference member indicates that referred (possibly polymorphic)
object will be same from referring object's birth to death /and/
referring object has no valid state without the referred object.
Like when component needs a reference to whole composite /and/
component can't exist without composite.

Optional reference feels sort of pointless. When referred (possibly
polymorphic) object will be same from referring object's birth to
death but referring object may exist without the referred object
then '* const' (or some constant smart pointer) is natural choice
since it can be constructed 'nullptr', no need for boost::eek:ptional.

Your example code is confusing since optional string member is
usually nothing like that. string is not polymorphic and it is cheap
to copy it and so on most cases people just use string const data
member (and empty string means "missing") again no need for
boost::eek:ptional.

boost::eek:ptional is usually used to get rid of indirection when it
is not needed (indirection is mostly needed when the referred object
is big, hard to copy or polymorphic).
 
L

Luca Risolia

Luca said:
K. Frank said:
It seems to me that for the use case I have in mind,
using a pointer or an optional would be the way to go.

Leaving aside the fact that optional is not yet standard,
is there any reason to prefer one over the other?
Maybe using the old-fashioned pointer solution is the
cleanest way to go here.

optional is safer. Also, in general, another reason is the following (taken
from the boost::eek:ptional documentation):

" [...] "

...I said "in general", because the latter reason usually makes more sense if
you choose the optional object to be a string instead of a reference to a
string.
 
L

Luca Risolia

Öö Tiib said:
Optional reference feels sort of pointless. When referred (possibly
polymorphic) object will be same from referring object's birth to
death but referring object may exist without the referred object
then '* const' (or some constant smart pointer) is natural choice
since it can be constructed 'nullptr', no need for boost::eek:ptional.

There is one substantial advantage with boost::eek:ptional about safety.

When using a pointer there is always the risk to dereference a nullptr. Now,
while dereferencing a nullptr is Undefined Behaviour, getting the object from
an uninitialized optional throws an exception. This means that only in the
latter case you can be sure you are doing something wrong.
 
J

James Kanze

First off, let me thank Luca for directing me to
boost::eek:ptional / std::eek:ptional. That looks like
the semantics I am looking for.
Let me answer several questions by several respondents
in this one post, below.
Answers to questions raised in previous posts:
Indeed. This looks like the best answer, given
the specific question I asked.
It's interesting to note that the c++14 proposal
for std::eek:ptional leaves open whether optional
references should be supported:

Probably because it already exists. An "optional" reference is
called a pointer. (Except that a pointer is more efficient, and
less complicated.)
There are a couple of possible reasons. (Again, this
is just an example.)
One is to stick with style that says to pass class
objects by const reference rather than value. (A
style that I believe James promotes.)

I've not so much promoted it, as simply pointed out that it is
ubiquitous, to the point where anything else causes the reader
to ask questions. But the issue here isn't "passing" the value;
I have no problems with the parameter to the constructor being
a const reference. The issue is a member, in an object which
might outlive the object referred to by the constructor.
If the value being passed in were bulkier than a
(short) string, passing by (const) reference could
be significantly cheaper. (But if the default value
of the class were comparably bulky, the use of the
dummy variable could vitiate any savings.)
And (in the analogous non-const case) one would
want a reference if the original passed-in argument
were to be modified.

*IF* the semantics of the class are to modify something else,
then using a reference or a pointer in the class is necessary.
In general, however, it's something to avoid.
Yes, using a pointer, with the possibility of initializing
it to a nullptr value, seems very reasonable and natural.
Are there any reasons to prefer boost/std::eek:ptional over
the tried-and-true pointer approach? Are there any benefits
(if only syntactic sugar) to the caller of the constructor?

No.

I've had my own Fallible class since long before
boost::eek:ptional. Practically speaking, it is useful for
returning values which can fail, or (as a mutable member) for
cached values. Or in very rare cases, as a parameter, since it
doesn't require an lvalue (as a pointer would). I've never
really used it as an optional value, however.
Ach, James, you of all people! Just the other day you
were expounding upon the virtues of passing strings by
const reference:
Speed of passing a string by value vs. const reference
https://groups.google.com/forum/#!topic/comp.lang.c++/0UuKuYp0fqY
Kidding aside, obviously for a (short) string, passing
by value would work just fine and would avoid the
uninitialized reference issue. But in use cases where
the object being passed in was too expensive to copy or
needed to be modified, pass-by-value wouldn't be appropriate.

Pass by reference has nothing to do with member reference.
Something passed by const reference will be used in the
function, or a copy will be made, so there can be no issues
involving dangling references (usually---one can easily
construct cases where there could be a problem, but they tend
not to occur naturally).

Consider for a moment the state your object will be left in if
I do:

Handle* h = new Handle( "lable" );
It seems to me that for the use case I have in mind,
using a pointer or an optional would be the way to go.

A pointer will have the same problems as a reference (except
that it won't render the class unassignable). It can dangle.
Optional is probably extra overhead here (not to mention that
boost::eek:ptional has a very poor interface). Just use a value,
with the empty string if it isn't being used. (If you need an
extra variable to specify which type is active, it should be
a separate enum type, and not bound to the string variable, when
it is equally relevant to the int.)
Leaving aside the fact that optional is not yet standard,
is there any reason to prefer one over the other?
Maybe using the old-fashioned pointer solution is the
cleanest way to go here.

If you like dangling pointers.
 
Ad

Advertisements

J

James Kanze

]
boost::eek:ptional is usually used to get rid of indirection when it
is not needed (indirection is mostly needed when the referred object
is big, hard to copy or polymorphic).

By far the most important use for things like boost::eek:ptional is
in return values from functions which may fail. (That's why it
has always been called Fallible in the past.) If the function
is a lookup, and returns something which is guaranteed to exist
beyond the function's lifetime, you return a pointer, but if the
function has to synthesize the return value, there's nothing
with sufficient lifetime that it could point to. So you return
a Fallible.

About the only other use is as a mutable member, for cached
values.

I'd be very sceptical of any other use.
 
J

James Kanze

Öö Tiib wrote:
There is one substantial advantage with boost::eek:ptional about safety.
When using a pointer there is always the risk to dereference a nullptr. Now,
while dereferencing a nullptr is Undefined Behaviour, getting the object from
an uninitialized optional throws an exception. This means that only in the
latter case you can be sure you are doing something wrong.

That sounds more like an advantage for the pointer. If you have
an error in your code, you want it to crash, not to throw an
exception.
 
Ö

Öö Tiib

There is one substantial advantage with boost::eek:ptional about safety.

When using a pointer there is always the risk to dereference a nullptr. Now,
while dereferencing a nullptr is Undefined Behaviour, getting the object from
an uninitialized optional throws an exception. This means that only in the
latter case you can be sure you are doing something wrong.

That is certainly advantage but I would like to discuss how "substantial".

With a raw pointer that dereference is made undefined behavior because
protection of memory might be overly expensive for some platforms. On most platforms such dereference does in practice result with rather guaranteed
access violation crash.

IMHO on most cases a crash is actually /lot/ more desirable behavior
than exception. If exception is desirable then no one forbids us to
implement smart pointer with well-documented dereference of nullptr for
such rare case.
 
Ö

Öö Tiib

On Monday, 12 August 2013 17:51:11 UTC+3, K. Frank wrote: [...]

boost::eek:ptional is usually used to get rid of indirection when it
is not needed (indirection is mostly needed when the referred object
is big, hard to copy or polymorphic).

By far the most important use for things like boost::eek:ptional is
in return values from functions which may fail. (That's why it
has always been called Fallible in the past.)

If the function is a lookup, and returns something which is guaranteed
to exist beyond the function's lifetime, you return a pointer, but if
the function has to synthesize the return value, there's nothing
with sufficient lifetime that it could point to. So you return
a Fallible.

Functions that may non-exceptionally fail and have to return some values
of a type that does not have "missing" or "null" states (like pointers).
About the only other use is as a mutable member, for cached values.

I'd be very sceptical of any other use.

How about mutable lazily initialized members? Expensive to initialize
and not guaranteed that it is ever needed.
 
J

James Kanze

On Monday, 12 August 2013 17:51:11 UTC+3, K. Frank wrote: [...]

boost::eek:ptional is usually used to get rid of indirection when it
is not needed (indirection is mostly needed when the referred object
is big, hard to copy or polymorphic).
By far the most important use for things like boost::eek:ptional is
in return values from functions which may fail. (That's why it
has always been called Fallible in the past.)
If the function is a lookup, and returns something which is guaranteed
to exist beyond the function's lifetime, you return a pointer, but if
the function has to synthesize the return value, there's nothing
with sufficient lifetime that it could point to. So you return
a Fallible.
Functions that may non-exceptionally fail and have to return some values
of a type that does not have "missing" or "null" states (like pointers).

Yes. That's what the class was designed for originally. In
Barton and Nackman.
How about mutable lazily initialized members? Expensive to initialize
and not guaranteed that it is ever needed.

That's what I meant be cached values. Although it doesn't have
to be just initialization. If the value depends on other
members of the class, you can invalidate the cache each time one
of the other members changes, and recalculate it on an as needed
basis.
 
Ad

Advertisements

Ö

Öö Tiib

That's what I meant be cached values. Although it doesn't have
to be just initialization. If the value depends on other
members of the class, you can invalidate the cache each time one
of the other members changes, and recalculate it on an as needed
basis.

Yes, too lot of patterns/idioms and way more names. My bad English
sometimes does not suggest what names mean same patterns or idioms.

As "cache" I usually imagine some treasure storage is involved for
"precious" values that are expensive to generate but for what unique
key is available or cheap to calculate. So when such a value is
needed then it is first searched from cache.

When it is not needed anymore then it is still kept in that cache for
a while (if there is room) since it may be is needed again. That I
do not call "lazy destruction" while technically it is.

If precious values are additionally mutable then such "cache" can
be double efficient when applied together with "copy on write".
"Copy on write" adds indirection and reference count that usually
fit very well to expensive to generate things.

If pattern/idiom does not involve such storage then I call it just
"lazily initialized" or "initialized on-demand". So I am here in
sort of linguistic trouble, I lack mental image of "cache" in that
"optional" so something I misunderstand about "cached" (or optional).
 
D

darylew

Suppose I have a class that has a member reference
variable, and sometimes I want to initialize it,
and sometimes I don't. How much sense does it make
to initialize it to a dummy member variable (to shut
the compiler up)?

As a concrete example, let's say I have a class that
is constructed with a value of or reference to some kind
of handle, but the handle might be a number or it might
be a string:

struct Handle {
Handle (int iHandle) :
iHandle_(iHandle),
sHandle_(dummy),
useIHandle_(true)
{}
Handle (const std::string& sHandle) :
iHandle_(0),
sHandle_(sHandle),
useIHandle_(false)
{}
const int iHandle_;
const std::string& sHandle_;
bool useIHandle_;
std::string dummy_;
}

The point is that the member reference variable sHandle_
is supposed to be initialized (in the constructors'
initialization lists), whether or not it's actually
going to be used. The constructor that takes an int
argument doesn't have any std::strings floating
around with which to initialize sHandle_, hence the
introduction of the member variable dummy_.

Is this a reasonable approach? Is there an established
idiom for doing this kind of thing?

First, when you're using your object in string mode, where are your letters
being stored?! You're NOT storing them at all; you're just re-using the data
from the source object. When that object dies, your letters die and you have
a dangling reference! That's bad, and you should store the string by value,
just like the integer. (And you can't tell what the remote's lifetime is; it
could be a global that outlasts your object, or a temporary that'll die
soon.[1])

Second, why is your primary data const? Do you actually need to override the
default const/volatile status for you sub-objects? (Said default is to match
the const/volatile state of the enclosing object.) Said overrides mess up
some automatic actions, and making all your external methods const serves the
same purpose.

For your main problem: you need to use either an integer or a string. And
neither both together nor none at all; always exactly one. If you're using
C++11, this is a job for a union.

// Not tested
class Handle {
union MyData {
int i;
std::string s;
MyData( int i = 0 ) : i{ i } {}
MyData( std::string const &s ) : s{ s } {}
MyData( std::string &&s ) : s{ std::move(s) } {}
} data;
bool useStr;

public:
Handle( int i ) : data{ i }, useStr{ false } {}
Handle( std::string const &s ) : data{ s }, useStr{ true } {}
Handle( std::string &&s ) : data{ std::move(s) }, useStr{ true } {}

Handle( Handle const &that );
Handle( Handle &&that );
~Handle();

void swap( Handle &that );

Handle & operator =( Handle that )
{ this->swap(that); }
//...
};

In previous versions of C++, a union could not have a member if any of its
special member functions were non-trivial. All of std::string's s.m.f.s are
non-trivial, so you have to use one of the aforementioned solutions if you're
using a pre-11 compiler. For C++11, non-trivial types can be in a union, but
each special member function is deleted (i.e. cancelled) unless all its
members' versions of that s.m.f. is trivial. Handle will start off with NO
s.m.f.s available until we add them in manually.

By default, using a union member in a class's initializer list will initialize
its first member. Lexically-later members cannot be initialized this way. An
empty initializer will do default initialization on said first member, but that
can't happen here since default initialization is cancelled for this union
(since std::string's version is non-trivial). The way around that is to add
constructors to the union. This idiom (tagged union) usually has the union
member's type anonymous, but I have to name the type in order to add
constructors. I can initialize the union with an integer, a string via copy,
or a string via move. The integer constructor also acts as a default one;
I'll explain that later.

My copy of your constructors just forward to the union's version. The
string-move constructor works similarly. All of them set the Boolean flag for
integer versus string.

I don't know how reference members work with copy-constructors, but your
Handle's version should work if references are supported. For my version, we
copy the flag and manually copy the data.

// Not tested
Handle::Handle( Handle const &that )
: useStr{ that.useStr }
{
if ( useStr )
new ( &data.s ) decltype( data.s ){ that.data.s };
else
data.i = that.data.i;
}

We can't copy the data member within the member initialization list, since
there's two choices and the decision is run-time based. That's why I added a
default-constructor to the union type. Since the union is set to be an integer
by default, I can do a simple assignment when the source object is in integer
mode. When the source is in string mode, I allocate the string's
copy-constructor call in place with placement-new. (Technically, I should have
called the "int" destructor for that memory segment first, but I can get away
without doing it because "int" has a trivial destructor.)

// Not tested
Handle::Handle( Handle &&that )
: useStr{ that.useStr }
{
if ( useStr )
new ( &data.s ) decltype( data.s ){ std::move(that.data.s) };
else
data.i = std::move( that.data.i );
}

The move constructor is similar. I couldn't do it at all if I left the primary
data const....

// Not tested
Handle::~Handle()
{
if ( useStr )
data.s.~decltype( data.s );
else
data.i.~decltype( data.i );
}

In the destructor, manually call the current object's constructor. I'm not
sure that using a "decltype" expression is legal as a form of a destructor
(name), or if it's legal but not supported by today's compilers. You could
leave off the "else" clause, since that member's destructor is trivial.

void Handle::swap( Handle &that );

I punted implementing (proper) copy-assignment and move-assignment operators by
doing a by-value (copy) assignment and swap. I'm also too tired to write out
swap. The swap function holds the real action. Do a four-way if-else chain.
If you have two integers or strings, call the standard swap routine. If you
have a mix, copy both the integer and string, destruct both, and
placement-allocate the new sub-objects. Since "int" is trivial, you can skip
some steps. However, it's nice to make swapping never-throw if possible, so
wrap the allocations in try-catch and mind your action order. (Actually, you
can skip wrapping the "int" constructor. And if you're super paranoid, you
could wrap the string destructor.)

Daryle W.


[1] It'll still outlast your object if the latter is a temporary too.
 
D

darylew

On Sunday, August 18, 2013 4:10:27 AM UTC-4, (e-mail address removed) wrote:
[SNIP]
void Handle::swap( Handle &that );

I punted implementing (proper) copy-assignment and move-assignment operators by
doing a by-value (copy) assignment and swap. I'm also too tired to write out
swap. The swap function holds the real action. Do a four-way if-else chain.
If you have two integers or strings, call the standard swap routine. If you
have a mix, copy both the integer and string, destruct both, and
placement-allocate the new sub-objects. Since "int" is trivial, you can skip
some steps. However, it's nice to make swapping never-throw if possible, so
wrap the allocations in try-catch and mind your action order. (Actually, you
can skip wrapping the "int" constructor. And if you're super paranoid, you
could wrap the string destructor.)

// Not tested
if ( not useStr and that.useStr ) {
auto const temp = data.i;

new ( &data.s ) decltype( data.s ) { std::move(that.data.s) };
that.data.s.~decltype( data.s )();
that.data.i = temp;
std::swap( useStr, that.useStr );
}

Hope that the constructor in the "new" call is either no-throw or has rollback
upon throw (i.e. the strong guarantee). Really hope that the destructor call
on the next line is never-fail (which includes no-throw). Of course, switch
the objects when doing the code for the reversed-flags case.

Daryle W.
 
Ad

Advertisements

J

James Kanze

For your main problem: you need to use either an integer or a string. And
neither both together nor none at all; always exactly one. If you're using
C++11, this is a job for a union.

// Not tested
class Handle {
union MyData {
int i;
std::string s;
MyData( int i = 0 ) : i{ i } {}
MyData( std::string const &s ) : s{ s } {}
MyData( std::string &&s ) : s{ std::move(s) } {}
} data;
bool useStr;
public:
Handle( int i ) : data{ i }, useStr{ false } {}
Handle( std::string const &s ) : data{ s }, useStr{ true } {}
Handle( std::string &&s ) : data{ std::move(s) }, useStr{ true } {}
Handle( Handle const &that );
Handle( Handle &&that );
~Handle();
void swap( Handle &that );
Handle & operator =( Handle that )
{ this->swap(that); }
//...
};
In previous versions of C++, a union could not have a member
if any of its special member functions were non-trivial. All
of std::string's s.m.f.s are non-trivial, so you have to use
one of the aforementioned solutions if you're using a pre-11
compiler. For C++11, non-trivial types can be in a union, but
each special member function is deleted (i.e. cancelled)
unless all its members' versions of that s.m.f. is trivial.
Handle will start off with NO s.m.f.s available until we add
them in manually.

What does the destructor of the union do in this case? If the
last field used was the string, then it needs destruction; if
the last field used was the int, then destruction of the string
element would be undefined behavior. According to the standard,
if and non-static data member of the union has a non-trival
destructor, the corresponding member function of the union must
be user-provided or it will be implicitly deleted for the union.
Implicitly deleting a destructor means that the only thing you
can do with the resulting union is to allocate it dynamically,
and never delete it.

To use the union in a class, you need to provide a user defined
destructor. But don't ask me what it should do. It will have
to use an explicit destructor call to destruct the string
member, but only if the string member was the active member
(which is information it doesn't have).

In fact, what he needs isn't a union, but a variant (from
Boost, or hand written, if you can't use Boost); a discriminate
union, if you prefer.

For the rest: VC++ doesn't support this feature of C++11 yet,
so portability would be limited. With g++ 4.7.2, it does work
if I define the union as:

union U
{
int i;
std::string s;
U() : i() {}
~U() {}
};

and do everything with placement new/explicit delete on U::i or
U::s (exactly as you would if you were implementing Variant
yourself). Globally, this feature does make the implementation
of Variant somewhat cleaner---you loose a lot of
reinterpret_cast, and additional members necessary to
(hopefully) ensure alignment. But it probably shouldn't appear
elsewhere; it's too tricky to get right.
 
Ad

Advertisements


Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top