Using explicit constructors for C++0x "return { expr };"

Discussion in 'C++' started by Johannes Schaub (litb), Sep 27, 2010.

  1. I'm wondering about this one:

    struct ALongAndComplexTypeName {
    explicit A(int);
    };

    /* Possibly intermixed with enable_if huh */
    ALongAndComplexTypeName f() {
    return {5};
    }

    I would like to do something like that to avoid having to repeat the return
    type name. Since return value copying forbids using explicit copy
    constructors, the above will fail. Is there any work around?
     
    Johannes Schaub (litb), Sep 27, 2010
    #1
    1. Advertising

  2. * Johannes Schaub (litb), on 27.09.2010 04:47:
    > I'm wondering about this one:
    >
    > struct ALongAndComplexTypeName {
    > explicit A(int);
    > };
    >
    > /* Possibly intermixed with enable_if huh */
    > ALongAndComplexTypeName f() {
    > return {5};
    > }
    >
    > I would like to do something like that to avoid having to repeat the return
    > type name. Since return value copying forbids using explicit copy
    > constructors, the above will fail. Is there any work around?


    <code>
    namespace detail {
    struct A // ALongAndComplexTypename
    {
    //A( A const& );
    //A& operator=( A const& );

    explicit A( int ) {}
    };

    A f() { return A( 5 ); }
    }

    typedef detail::A ALongAndComplexTypename;
    using detail::f;

    int main()
    {
    f();
    }
    </code>

    But if you're not willing to put up with ALongAndComplexTypename in your own
    implementation code, why are you foisting that name on client code?

    Apart from that,


    Cheers, & enjoy!,

    - Alf

    --
    blog at <url: http://alfps.wordpress.com>
     
    Alf P. Steinbach /Usenet, Sep 27, 2010
    #2
    1. Advertising

  3. Johannes Schaub (litb)

    Luc Danton Guest

    On 27/09/2010 06:39, Alf P. Steinbach /Usenet wrote:
    > * Johannes Schaub (litb), on 27.09.2010 04:47:
    >> I'm wondering about this one:
    >>
    >> struct ALongAndComplexTypeName {
    >> explicit A(int);
    >> };
    >>
    >> /* Possibly intermixed with enable_if huh */
    >> ALongAndComplexTypeName f() {
    >> return {5};
    >> }
    >>
    >> I would like to do something like that to avoid having to repeat the
    >> return
    >> type name. Since return value copying forbids using explicit copy
    >> constructors, the above will fail. Is there any work around?

    >
    > <code>
    > namespace detail {
    > struct A // ALongAndComplexTypename
    > {
    > //A( A const& );
    > //A& operator=( A const& );
    >
    > explicit A( int ) {}
    > };
    >
    > A f() { return A( 5 ); }
    > }
    >
    > typedef detail::A ALongAndComplexTypename;
    > using detail::f;
    >
    > int main()
    > {
    > f();
    > }
    > </code>
    >
    > But if you're not willing to put up with ALongAndComplexTypename in your
    > own implementation code, why are you foisting that name on client code?
    >
    > Apart from that,
    >
    >
    > Cheers, & enjoy!,
    >
    > - Alf
    >


    I suspect what was meant by a long typename is something like

    typename std::conditional<
    some_check<some_template_param>::value,
    something,
    something_else
    >::type


    where something & something else could both be constructed from the same
    expression but are not convertible from one to the other.

    In that (possibly contrived) case, you'd need to repeat yourself in the
    body:

    {
    /* whatever */
    typedef typename std::conditional<
    some_check<some_template_param>::value,
    something,
    something_else
    >::type ret_t;
    return ret_t { some_expression };
    }
     
    Luc Danton, Sep 27, 2010
    #3
  4. * Luc Danton, on 27.09.2010 07:28:
    > On 27/09/2010 06:39, Alf P. Steinbach /Usenet wrote:
    >> * Johannes Schaub (litb), on 27.09.2010 04:47:
    >>> I'm wondering about this one:
    >>>
    >>> struct ALongAndComplexTypeName {
    >>> explicit A(int);
    >>> };
    >>>
    >>> /* Possibly intermixed with enable_if huh */
    >>> ALongAndComplexTypeName f() {
    >>> return {5};
    >>> }
    >>>
    >>> I would like to do something like that to avoid having to repeat the
    >>> return
    >>> type name. Since return value copying forbids using explicit copy
    >>> constructors, the above will fail. Is there any work around?

    >>
    >> <code>
    >> namespace detail {
    >> struct A // ALongAndComplexTypename
    >> {
    >> //A( A const& );
    >> //A& operator=( A const& );
    >>
    >> explicit A( int ) {}
    >> };
    >>
    >> A f() { return A( 5 ); }
    >> }
    >>
    >> typedef detail::A ALongAndComplexTypename;
    >> using detail::f;
    >>
    >> int main()
    >> {
    >> f();
    >> }
    >> </code>
    >>
    >> But if you're not willing to put up with ALongAndComplexTypename in your
    >> own implementation code, why are you foisting that name on client code?
    >>
    >> Apart from that,
    >>
    >>
    >> Cheers, & enjoy!,
    >>
    >> - Alf
    >>

    >
    > I suspect what was meant by a long typename is something like
    >
    > typename std::conditional<
    > some_check<some_template_param>::value,
    > something,
    > something_else
    > >::type

    >
    > where something & something else could both be constructed from the same
    > expression but are not convertible from one to the other.
    >
    > In that (possibly contrived) case, you'd need to repeat yourself in the body:
    >
    > {
    > /* whatever */
    > typedef typename std::conditional<
    > some_check<some_template_param>::value,
    > something,
    > something_else
    > >::type ret_t;

    > return ret_t { some_expression };
    > }


    Uh, I was going to ask why you all are using needless C++0x syntax, but then I
    discovered the thread's subject line.

    Just a hint: when using MoronBird 3.x[1] you can avoid some of the quoting bugs
    by selecting all the text before hitting "reply".

    Anyways, I recall vaguely that in C++0x one can write something after the
    function head? And perhaps there's a result_of or something in the standard lib?
    Too busy to check right now, but worth checking I think! :)


    Cheers,

    - Alf

    Notes:
    [1] In versions 1.x known as ThunderBird.

    --
    blog at <url: http://alfps.wordpress.com>
     
    Alf P. Steinbach /Usenet, Sep 27, 2010
    #4
  5. Johannes Schaub (litb)

    Luc Danton Guest

    On 27/09/2010 08:38, Alf P. Steinbach /Usenet wrote:
    > * Luc Danton, on 27.09.2010 07:28:
    >> On 27/09/2010 06:39, Alf P. Steinbach /Usenet wrote:
    >>> * Johannes Schaub (litb), on 27.09.2010 04:47:
    >>>> I'm wondering about this one:
    >>>>
    >>>> struct ALongAndComplexTypeName {
    >>>> explicit A(int);
    >>>> };
    >>>>
    >>>> /* Possibly intermixed with enable_if huh */
    >>>> ALongAndComplexTypeName f() {
    >>>> return {5};
    >>>> }
    >>>>
    >>>> I would like to do something like that to avoid having to repeat the
    >>>> return
    >>>> type name. Since return value copying forbids using explicit copy
    >>>> constructors, the above will fail. Is there any work around?
    >>>
    >>> <code>
    >>> namespace detail {
    >>> struct A // ALongAndComplexTypename
    >>> {
    >>> //A( A const& );
    >>> //A& operator=( A const& );
    >>>
    >>> explicit A( int ) {}
    >>> };
    >>>
    >>> A f() { return A( 5 ); }
    >>> }
    >>>
    >>> typedef detail::A ALongAndComplexTypename;
    >>> using detail::f;
    >>>
    >>> int main()
    >>> {
    >>> f();
    >>> }
    >>> </code>
    >>>
    >>> But if you're not willing to put up with ALongAndComplexTypename in your
    >>> own implementation code, why are you foisting that name on client code?
    >>>
    >>> Apart from that,
    >>>
    >>>
    >>> Cheers, & enjoy!,
    >>>
    >>> - Alf
    >>>

    >>
    >> I suspect what was meant by a long typename is something like
    >>
    >> typename std::conditional<
    >> some_check<some_template_param>::value,
    >> something,
    >> something_else
    >> >::type

    >>
    >> where something & something else could both be constructed from the same
    >> expression but are not convertible from one to the other.
    >>
    >> In that (possibly contrived) case, you'd need to repeat yourself in
    >> the body:
    >>
    >> {
    >> /* whatever */
    >> typedef typename std::conditional<
    >> some_check<some_template_param>::value,
    >> something,
    >> something_else
    >> >::type ret_t;

    >> return ret_t { some_expression };
    >> }

    >
    > Uh, I was going to ask why you all are using needless C++0x syntax, but
    > then I discovered the thread's subject line.
    >
    > Just a hint: when using MoronBird 3.x[1] you can avoid some of the
    > quoting bugs by selecting all the text before hitting "reply".
    >
    > Anyways, I recall vaguely that in C++0x one can write something after
    > the function head? And perhaps there's a result_of or something in the
    > standard lib? Too busy to check right now, but worth checking I think! :)


    There is the delayed return type syntax yes.

    int func();
    can be written
    auto func() -> int;

    But its purpose is to have the parameters in scope (or declared? Not
    sure about the proper terminology):

    template<typename T>
    auto func(T t)
    -> decltype(*t);

    You still can't factor out a type via a typedef (or using decl.) before
    getting in the function body proper, where it's too late. std::result_of
    is of no help either (I think).

    However I've seen Boost.MPL use a "result_of" namespace, which doubles
    up not only as a helper for convenient typedefs, but is actually used
    for meta-computations. I think that's the proper way to "hide" those
    computations from the user (if that's needed) and make maintenance
    easier (but YMMV), and I use it too. So my own example would look like:

    namespace result_of {

    template</* whatever */>
    struct f {
    typedef typename std::conditional<
    some_value,
    something,
    something
    >::type type;
    };

    } // result_of

    // Back in enclosing namespace
    template</* whatever */>
    typename result_of::f< ... >::type
    f( ... )
    {
    ...
    typedef typename result_of::f< ... >::type ret_t;
    return ret_t { /* something */ };
    }


    Also thanks for the TB tip; although I never noticed anything wrong with
    the quotes.
     
    Luc Danton, Sep 27, 2010
    #5
  6. Luc Danton wrote:

    > On 27/09/2010 08:38, Alf P. Steinbach /Usenet wrote:
    >> * Luc Danton, on 27.09.2010 07:28:
    >>> On 27/09/2010 06:39, Alf P. Steinbach /Usenet wrote:
    >>>> * Johannes Schaub (litb), on 27.09.2010 04:47:
    >>>>> I'm wondering about this one:
    >>>>>
    >>>>> struct ALongAndComplexTypeName {
    >>>>> explicit A(int);
    >>>>> };
    >>>>>
    >>>>> /* Possibly intermixed with enable_if huh */
    >>>>> ALongAndComplexTypeName f() {
    >>>>> return {5};
    >>>>> }
    >>>>>
    >>>>> I would like to do something like that to avoid having to repeat the
    >>>>> return
    >>>>> type name. Since return value copying forbids using explicit copy
    >>>>> constructors, the above will fail. Is there any work around?
    >>>>
    >>>> <code>
    >>>> namespace detail {
    >>>> struct A // ALongAndComplexTypename
    >>>> {
    >>>> //A( A const& );
    >>>> //A& operator=( A const& );
    >>>>
    >>>> explicit A( int ) {}
    >>>> };
    >>>>
    >>>> A f() { return A( 5 ); }
    >>>> }
    >>>>
    >>>> typedef detail::A ALongAndComplexTypename;
    >>>> using detail::f;
    >>>>
    >>>> int main()
    >>>> {
    >>>> f();
    >>>> }
    >>>> </code>
    >>>>
    >>>> But if you're not willing to put up with ALongAndComplexTypename in
    >>>> your own implementation code, why are you foisting that name on client
    >>>> code?
    >>>>
    >>>> Apart from that,
    >>>>
    >>>>
    >>>> Cheers, & enjoy!,
    >>>>
    >>>> - Alf
    >>>>
    >>>
    >>> I suspect what was meant by a long typename is something like
    >>>
    >>> typename std::conditional<
    >>> some_check<some_template_param>::value,
    >>> something,
    >>> something_else
    >>> >::type
    >>>
    >>> where something & something else could both be constructed from the same
    >>> expression but are not convertible from one to the other.
    >>>
    >>> In that (possibly contrived) case, you'd need to repeat yourself in
    >>> the body:
    >>>
    >>> {
    >>> /* whatever */
    >>> typedef typename std::conditional<
    >>> some_check<some_template_param>::value,
    >>> something,
    >>> something_else
    >>> >::type ret_t;
    >>> return ret_t { some_expression };
    >>> }

    >>
    >> Uh, I was going to ask why you all are using needless C++0x syntax, but
    >> then I discovered the thread's subject line.
    >>
    >> Just a hint: when using MoronBird 3.x[1] you can avoid some of the
    >> quoting bugs by selecting all the text before hitting "reply".
    >>
    >> Anyways, I recall vaguely that in C++0x one can write something after
    >> the function head? And perhaps there's a result_of or something in the
    >> standard lib? Too busy to check right now, but worth checking I think!
    >> :)

    >
    > There is the delayed return type syntax yes.
    >
    > int func();
    > can be written
    > auto func() -> int;
    >
    > But its purpose is to have the parameters in scope (or declared? Not
    > sure about the proper terminology):
    >
    > template<typename T>
    > auto func(T t)
    > -> decltype(*t);
    >
    > You still can't factor out a type via a typedef (or using decl.) before
    > getting in the function body proper, where it's too late. std::result_of
    > is of no help either (I think).
    >


    One very ugly thing to do is to use default template arguments

    template<typename T, typename R = decltype(*declval<T>())>
    R func(T t) {
    return { *t };
    }
     
    Johannes Schaub (litb), Sep 27, 2010
    #6
  7. Johannes Schaub (litb)

    SG Guest

    On 27 Sep., 04:47, Johannes Schaub wrote:
    > I'm wondering about this one:
    >
    > struct ALongAndComplexTypeName {
    >   explicit A(int);
    > };
    >
    > /* Possibly intermixed with enable_if huh */
    > ALongAndComplexTypeName f() {
    >   return {5};
    > }
    >
    > I would like to do something like that to avoid having to repeat the return
    > type name. Since return value copying forbids using explicit copy
    > constructors, the above will fail.


    ALongAndComplexTypeName *has* an implicit copy constructor but only an
    explicit constructor taking an int. I think you're right, the code
    won't work. The upcoming standard refers to this kind of
    initialization as a "copy-list-initialization" which I guess belongs
    to the copy-initialization category. So the explicit A(int) is not
    viable for this kind of initialization.

    After thinking about it, it seems that with C++0x's list
    initialization for non-aggregate classes the keyword 'export' has also
    an effect on constructors that take more than one parameter (ignoring
    defaut arguments) -- unlike before in C++03. I havn't realized this
    until now. So, if you don't want class type T

    class T {
    public:
    T(int, double, void*);
    };

    to be copy-list-initializable we can make this constructor explicit so
    that

    void foo() {
    T x = {1729,3.14159,nullptr};
    }

    won't compile anymore. Interesting.

    > Is there any work around?


    I don't see any. This doesn't really strike me as a big annoyance,
    though.

    Cheers!
    SG
     
    SG, Sep 27, 2010
    #7
  8. SG wrote:

    > On 27 Sep., 04:47, Johannes Schaub wrote:
    >> I'm wondering about this one:
    >>
    >> struct ALongAndComplexTypeName {
    >> explicit A(int);
    >> };
    >>
    >> /* Possibly intermixed with enable_if huh */
    >> ALongAndComplexTypeName f() {
    >> return {5};
    >> }
    >>
    >> I would like to do something like that to avoid having to repeat the
    >> return type name. Since return value copying forbids using explicit copy
    >> constructors, the above will fail.

    >
    > ALongAndComplexTypeName *has* an implicit copy constructor but only an
    > explicit constructor taking an int. I think you're right, the code
    > won't work. The upcoming standard refers to this kind of
    > initialization as a "copy-list-initialization" which I guess belongs
    > to the copy-initialization category. So the explicit A(int) is not
    > viable for this kind of initialization.
    >


    The worst, in my opinion, is that the constructor *is* viable, but it is not
    allowed to be used.

    struct A {
    explicit A(bool);
    A(std::string);
    };

    // error! explicit constructor used
    A a = { "hello folks" };

    // fine! A(bool) not viable
    A a = "hello folks";

    I dunno why that is desirable but it appears to me that it will have bad
    effects on overload resolution.

    struct Vector {
    explicit Vector(int len);
    };

    struct MyInt {
    MyInt(int I);
    };

    void print(Vector v);
    void print(MyInt i);

    // fine, call MyInt version
    print(42);

    // oops, ambiguity
    print({42});

    > After thinking about it, it seems that with C++0x's list
    > initialization for non-aggregate classes the keyword 'export' has also
    > an effect on constructors that take more than one parameter (ignoring
    > defaut arguments) -- unlike before in C++03. I havn't realized this
    > until now. So, if you don't want class type T
    >
    > class T {
    > public:
    > T(int, double, void*);
    > };
    >
    > to be copy-list-initializable we can make this constructor explicit so
    > that
    >
    > void foo() {
    > T x = {1729,3.14159,nullptr};
    > }
    >
    > won't compile anymore. Interesting.
    >


    I agree that this is intriguing, I have not thought about that case before.
    There was until recently a way to do the same for a zero-parameter default
    constructor

    struct T {
    explicit T();
    };

    // ill-formed! constructor is explicit
    T t = { };

    This was changed in the FCD though, because now it disregards overload
    resolution and just calls that constructor as part of value initialization.
    It wasn't all that useful anyway, I think :)
     
    Johannes Schaub (litb), Sep 27, 2010
    #8
  9. Johannes Schaub (litb)

    SG Guest

    On 27 Sep., 16:09, "Johannes Schaub (litb)" wrote:
    >
    > struct A {
    >   explicit A(bool);
    >   A(std::string);
    > };
    >
    > // error! explicit constructor used
    > A a = { "hello folks" };


    To my surprize I got the following error using G++ 4.4.1:

    otest.cpp: In function 'int main()':
    otest.cpp:13: error: call of overloaded 'A(<brace-enclosed
    initializer list>)' is ambiguous
    otest.cpp:8: note: candidates are: A::A(std::string)
    otest.cpp:6: note: A::A(const A&)

    I actually expected it to work and use A::A(string).
    Hmm...

    > // fine! A(bool) not viable
    > A a = "hello folks";


    This doesn't compile either using G++ 4.4.1:

    otest.cpp:13: error: conversion from 'const char [12]' to
    non-scalar type 'A' requested

    I also expected this to work and use A::A(string).

    > I dunno why that is desirable but it appears to me that it will have bad
    > effects on overload resolution.
    >
    > struct Vector {
    >   explicit Vector(int len);
    > };
    >
    > struct MyInt {
    >   MyInt(int I);
    > };
    >
    > void print(Vector v);
    > void print(MyInt i);
    >
    > // fine, call MyInt version
    > print(42);


    Ok, makes sense.

    > // oops, ambiguity
    > print({42});


    Why is this supposed to be an ambiguity?

    > SG wrote:
    > > After thinking about it, it seems that with C++0x's list
    > > initialization for non-aggregate classes the keyword 'export' has also
    > > an effect on constructors that take more than one parameter (ignoring
    > > defaut arguments) -- unlike before in C++03. I havn't realized this
    > > until now. So, if you don't want class type T


    Of course, I meant to write "explicit", not "export". :)

    > >   class T {
    > >   public:
    > >     T(int, double, void*);
    > >   };

    >
    > > to be copy-list-initializable we can make this constructor explicit so
    > > that

    >
    > >   void foo() {
    > >     T x = {1729,3.14159,nullptr};
    > >   }

    >
    > > won't compile anymore. Interesting.

    >
    > I agree that this is intriguing, I have not thought about that case before.
    > There was until recently a way to do the same for a zero-parameter default
    > constructor
    >
    > struct T {
    >  explicit T();
    > };
    >
    > // ill-formed! constructor is explicit
    > T t = { };


    Why would you even write this? T t{}; should do the trick.

    > This was changed in the FCD though, because now it disregards overload
    > resolution and just calls that constructor as part of value initialization.
    > It wasn't all that useful anyway, I think :)


    I currently don't claim to understand initialization. It sounds like
    an awful lot of special cases to me. And *I* thought, it's going to
    get more intuitive...

    Cheers!
    SG
     
    SG, Sep 27, 2010
    #9
  10. Johannes Schaub (litb) wrote:

    > SG wrote:
    >
    >> On 27 Sep., 16:09, "Johannes Schaub (litb)" wrote:
    >>>
    >>> struct A {
    >>> explicit A(bool);
    >>> A(std::string);
    >>> };
    >>>
    >>> // error! explicit constructor used
    >>> A a = { "hello folks" };

    >>
    >> To my surprize I got the following error using G++ 4.4.1:
    >>
    >> otest.cpp: In function 'int main()':
    >> otest.cpp:13: error: call of overloaded 'A(<brace-enclosed
    >> initializer list>)' is ambiguous
    >> otest.cpp:8: note: candidates are: A::A(std::string)
    >> otest.cpp:6: note: A::A(const A&)
    >>
    >> I actually expected it to work and use A::A(string).
    >> Hmm...
    >>

    >
    > Yes, I think this should use A::A(string). Copy-list-initialization has no
    > restriction onto nested user defined conversions as opposed to plain copy
    > initialization. I think the error here is that GCC4.4 does disregard
    > 13.3.3.1/4 which says "by 13.3.1.7 (when passing the initializer list as a
    > single argument or when the initializer list has exactly one element) and
    > (a conversion to some class X or reference to (possibly cv-qualiï¬ed) X is
    > considered for the ï¬rst parameter of a constructor of X)" (parens by me to
    > hilight the binding of "and" and "or" in the wording, which I find easily
    > confused because "and" usually binds tighter :)
    >
    > This is intended to factor out the copy constructor for list
    > initialization because since we are allowed to use nested user defined
    > conversions, we could always produce an ambiguous second conversion path
    > by first invoking the copy constructor and then doing the same as we did
    > for the other conversions.
    >


    Sorry I meant that it should error out because it uses the explicit
    constructor (standard conversion sequence is better than user defined
    conversion sequence). But that GCC4.4's ambiguity error message is out of
    order: It should not consider the copy constructor.
     
    Johannes Schaub (litb), Sep 27, 2010
    #10
  11. Alf P. Steinbach /Usenet <>, on
    27/09/2010 08:38:03, wrote:

    > Just a hint: when using [Thunder]Bird 3.x[1] you can avoid some of the
    > quoting bugs by selecting all the text before hitting "reply".


    Neat... from which cylinder did you pull that out?
    Stomped on it by casualty?

    Thanks for sharing!
    [and please glide on the edit]

    --
    Francesco S. Carta
    http://fscode.altervista.org
     
    Francesco S. Carta, Oct 1, 2010
    #11
    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. J.T. Conklin
    Replies:
    1
    Views:
    445
    David Hilsee
    Aug 11, 2004
  2. Dave
    Replies:
    2
    Views:
    419
    Ioannis Vranos
    Jan 15, 2005
  3. Andy
    Replies:
    5
    Views:
    512
    Shezan Baig
    Jan 30, 2005
  4. D.J. Stachniak

    Circumventing explicit constructors

    D.J. Stachniak, Nov 15, 2005, in forum: C++
    Replies:
    9
    Views:
    394
    Old Wolf
    Nov 15, 2005
  5. Replies:
    1
    Views:
    583
    Salt_Peter
    Dec 25, 2006
Loading...

Share This Page