Initializing member references to dummy member variables

Discussion in 'C++' started by K. Frank, Aug 12, 2013.

  1. K. Frank

    K. Frank Guest

    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
    K. Frank, Aug 12, 2013
    #1
    1. Advertising

  2. K. Frank

    Luca Risolia Guest

    K. Frank wrote:

    > 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';
    }
    Luca Risolia, Aug 12, 2013
    #2
    1. Advertising

  3. K. Frank

    Ike Naar Guest

    On 2013-08-12, K. Frank <> wrote:
    > 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_;
    }
    >
    Ike Naar, Aug 12, 2013
    #3
  4. Hello K, Frank!

    On 08/11/2013 08:29 PM, K. Frank wrote:
    > 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.
    Norman J. Goldstein, Aug 12, 2013
    #4
  5. K. Frank

    James Kanze Guest

    On Monday, 12 August 2013 04:29:56 UTC+1, K. Frank wrote:

    > 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.

    --
    James
    James Kanze, Aug 12, 2013
    #5
  6. K. Frank

    K. Frank Guest

    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.

    On Monday, August 12, 2013 7:35:01 AM UTC-4, James Kanze wrote:
    > On Monday, 12 August 2013 04:29:56 UTC+1, K. Frank wrote:
    >
    > > Suppose I have a class that has a member reference
    > > variable, and sometimes I want to initialize it,
    > > and sometimes I don't.

    > ...
    >
    > > 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_.

    > ...


    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.

    > James


    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
    K. Frank, Aug 12, 2013
    #6
  7. K. Frank

    Luca Risolia Guest

    K. Frank wrote:

    > 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. "
    Luca Risolia, Aug 12, 2013
    #7
  8. K. Frank

    Öö Tiib Guest

    On Monday, 12 August 2013 17:51:11 UTC+3, K. Frank wrote:
    > 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).
    Öö Tiib, Aug 12, 2013
    #8
  9. K. Frank

    Luca Risolia Guest

    Luca Risolia wrote:

    > K. Frank wrote:
    >
    >> 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.
    Luca Risolia, Aug 12, 2013
    #9
  10. K. Frank

    Luca Risolia Guest

    Öö Tiib wrote:

    > 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.
    Luca Risolia, Aug 12, 2013
    #10
  11. K. Frank

    James Kanze Guest

    On Monday, 12 August 2013 15:51:11 UTC+1, K. Frank wrote:

    > 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.


    > On Monday, August 12, 2013 7:35:01 AM UTC-4, James Kanze wrote:
    > > On Monday, 12 August 2013 04:29:56 UTC+1, K. Frank wrote:


    > > > Suppose I have a class that has a member reference
    > > > variable, and sometimes I want to initialize it,
    > > > and sometimes I don't.

    > > ...


    > > > 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_.

    > > ...


    > 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


    Probably because it already exists. An "optional" reference is
    called a pointer. (Except that a pointer is more efficient, and
    less complicated.)

    > > 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.)


    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.

    > > 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?


    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.

    > > 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.


    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.

    --
    James
    James Kanze, Aug 12, 2013
    #11
  12. K. Frank

    James Kanze Guest

    On Monday, 12 August 2013 16:59:59 UTC+1, Öö Tiib wrote:
    > 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.

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

    I'd be very sceptical of any other use.

    --
    James
    James Kanze, Aug 12, 2013
    #12
  13. K. Frank

    James Kanze Guest

    On Monday, 12 August 2013 18:00:57 UTC+1, Luca Risolia wrote:
    > Öö Tiib wrote:


    > > 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.


    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.

    --
    James
    James Kanze, Aug 12, 2013
    #13
  14. K. Frank

    Öö Tiib Guest

    On Monday, 12 August 2013 20:00:57 UTC+3, Luca Risolia wrote:
    > Öö Tiib wrote:
    > > 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.


    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, Aug 12, 2013
    #14
  15. K. Frank

    Öö Tiib Guest

    On Monday, 12 August 2013 21:34:10 UTC+3, James Kanze wrote:
    > On Monday, 12 August 2013 16:59:59 UTC+1, Öö Tiib wrote:
    > > 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.
    Öö Tiib, Aug 12, 2013
    #15
  16. K. Frank

    James Kanze Guest

    On Monday, 12 August 2013 20:17:28 UTC+1, Öö Tiib wrote:
    > On Monday, 12 August 2013 21:34:10 UTC+3, James Kanze wrote:
    > > On Monday, 12 August 2013 16:59:59 UTC+1, Öö Tiib wrote:
    > > > 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.

    > > 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.


    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.

    --
    James
    James Kanze, Aug 13, 2013
    #16
  17. K. Frank

    Öö Tiib Guest

    On Tuesday, 13 August 2013 18:27:25 UTC+3, James Kanze wrote:
    > On Monday, 12 August 2013 20:17:28 UTC+1, Öö Tiib wrote:
    > > On Monday, 12 August 2013 21:34:10 UTC+3, James Kanze wrote:


    > > > 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.


    > 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).
    Öö Tiib, Aug 14, 2013
    #17
  18. K. Frank

    Guest

    On Sunday, August 11, 2013 11:29:56 PM UTC-4, K. Frank wrote:

    > 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.
    , Aug 18, 2013
    #18
  19. K. Frank

    Guest

    On Sunday, August 18, 2013 4:10:27 AM UTC-4, 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.
    , Aug 18, 2013
    #19
  20. K. Frank

    James Kanze Guest

    On Sunday, 18 August 2013 09:10:27 UTC+1, wrote:
    > On Sunday, August 11, 2013 11:29:56 PM UTC-4, K. Frank wrote:


    > 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.

    --
    James
    James Kanze, Aug 19, 2013
    #20
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Replies:
    3
    Views:
    433
    Victor Bazarov
    Nov 10, 2004
  2. 2b|!2b==?
    Replies:
    5
    Views:
    332
    James Kanze
    May 2, 2007
  3. Grey Alien
    Replies:
    8
    Views:
    385
    Alf P. Steinbach
    Jul 24, 2007
  4. Replies:
    9
    Views:
    924
  5. Tim Johnson
    Replies:
    1
    Views:
    241
    Steven D'Aprano
    Jun 13, 2011
Loading...

Share This Page