decltype and const reference params

Discussion in 'C++' started by Noah Roberts, Apr 7, 2010.

  1. Noah Roberts

    Noah Roberts Guest

    I'm playing around with C++0x features and have run into something
    mildly interesting/irritating:

    template < typename T >
    struct point
    {
    T x,y;
    point(T t1, T t2) : x(t1), y(t2) {}
    };

    template < typename T1, typename T2 >
    auto operator + (point<T1> const& lh, point<T2> const& rh)
    -> point< decltype(lh.x + rh.x) >
    {
    return point< decltype(lh.x + rh.x) >(lh.x + rh.x, lh.y + rh.y);
    }

    int main()
    {
    point<int> pt1(5,5);
    point<double> pt2(4.2,6.66);

    point<double> pt3 = pt1 + pt2; // op + returned point<double const>
    }

    What is the best way to go about fixing this? I can think of a few ok
    solutions but I wonder also if there's something, maybe with rvalue
    references, that would solve it elegantly.

    --
    http://crazyeddiecpp.blogspot.com/
    Noah Roberts, Apr 7, 2010
    #1
    1. Advertising

  2. Noah Roberts

    Noah Roberts Guest

    In article <>,
    says...

    > template < typename T1, typename T2 >
    > auto operator + (point<T1> const& lh, point<T2> const& rh)
    > -> point< decltype(lh.x + rh.x) >
    > {
    > return point< decltype(lh.x + rh.x) >(lh.x + rh.x, lh.y + rh.y);
    > }


    Rewrote the above as:

    template < typename T1, typename T2 >
    decltype(T1() + T2()) operator + (.....)
    {
    return point< decltype(T1() + T2()) >(....);
    }

    works this way. Not sure if this adds overhead though.

    --
    http://crazyeddiecpp.blogspot.com/
    Noah Roberts, Apr 7, 2010
    #2
    1. Advertising

  3. Noah Roberts

    Joe Gottman Guest

    Noah Roberts wrote:
    > In article <>,
    > says...
    >
    >> template < typename T1, typename T2 >
    >> auto operator + (point<T1> const& lh, point<T2> const& rh)
    >> -> point< decltype(lh.x + rh.x) >
    >> {
    >> return point< decltype(lh.x + rh.x) >(lh.x + rh.x, lh.y + rh.y);
    >> }

    >
    > Rewrote the above as:
    >
    > template < typename T1, typename T2 >
    > decltype(T1() + T2()) operator + (.....)
    > {
    > return point< decltype(T1() + T2()) >(....);
    > }
    >
    > works this way. Not sure if this adds overhead though.
    >


    This doesn't add overhead, since the T1() and T2() are never
    actually called. No function inside a decltype statement ever is.

    Note that your code fails if T1 or T2 doesn't have a default
    constructor. To avoid this problem, C++0x will include a new template
    function:
    template <class T> T&& declval<T>().

    This horribly-named function can take any type T as its template
    parameter, including abstract classes, incomplete classes, and classes
    that don't have a copy or move constructor. The drawback is that it can
    only be used inside a decltype or sizeof statement, since it causes an
    immediate compile-error if it is used anywhere else. Thus, for a
    completely generic function you could redeclare your function as

    template <class T1, class T2>
    decltype(declval<T1>() + declval<T2>()) operator+() {...}

    Joe Gottman
    Joe Gottman, Apr 7, 2010
    #3
  4. Noah Roberts

    Noah Roberts Guest

    In article <PtQun.530382$1.easynews.com>,
    says...
    >
    > Noah Roberts wrote:
    > > In article <>,
    > > says...
    > >
    > >> template < typename T1, typename T2 >
    > >> auto operator + (point<T1> const& lh, point<T2> const& rh)
    > >> -> point< decltype(lh.x + rh.x) >
    > >> {
    > >> return point< decltype(lh.x + rh.x) >(lh.x + rh.x, lh.y + rh.y);
    > >> }

    > >
    > > Rewrote the above as:
    > >
    > > template < typename T1, typename T2 >
    > > decltype(T1() + T2()) operator + (.....)
    > > {
    > > return point< decltype(T1() + T2()) >(....);
    > > }
    > >
    > > works this way. Not sure if this adds overhead though.
    > >

    >
    > This doesn't add overhead, since the T1() and T2() are never
    > actually called. No function inside a decltype statement ever is.
    >
    > Note that your code fails if T1 or T2 doesn't have a default
    > constructor. To avoid this problem, C++0x will include a new template
    > function:
    > template <class T> T&& declval<T>().


    Yeah, I noticed this problem but didn't know about declval. Initial
    attempts to use it didn't fly but I might not be using the right header
    and/or namespace. Was easy enough to create, since it only needs to be
    declared, so even if it doesn't exist in this compiler it should work.

    Maybe you could help me figure out why this fails to compile though:

    template < typename T1, typename T2 >
    auto operator + (point<T1> && t1, point<T2> && t2)
    -> point< decltype(t1.x + t2.x) >
    {

    return point< decltype(t1.x + t2.x) >(t1.x + t2.x, t1.y + t2.y);
    }

    The compiler can't resolve operator + for point<T> when declared like
    this. It even complains about there not being a default constructor for
    point<double> (same driver code as my OP).

    --
    http://crazyeddiecpp.blogspot.com/
    Noah Roberts, Apr 7, 2010
    #4
  5. Noah Roberts <> writes:

    > I'm playing around with C++0x features and have run into something
    > mildly interesting/irritating:
    >
    > template < typename T >
    > struct point
    > {
    > T x,y;
    > point(T t1, T t2) : x(t1), y(t2) {}
    > };
    >
    > template < typename T1, typename T2 >
    > auto operator + (point<T1> const& lh, point<T2> const& rh)
    > -> point< decltype(lh.x + rh.x) >
    > {
    > return point< decltype(lh.x + rh.x) >(lh.x + rh.x, lh.y + rh.y);
    > }
    >
    > int main()
    > {
    > point<int> pt1(5,5);
    > point<double> pt2(4.2,6.66);
    >
    > point<double> pt3 = pt1 + pt2; // op + returned point<double const>
    > }
    >
    > What is the best way to go about fixing this? I can think of a few ok
    > solutions but I wonder also if there's something, maybe with rvalue
    > references, that would solve it elegantly.


    Given your problem (but see below) it depends upon what you mean by
    `fix'. One `solution' might be:

    auto pt3 = pt1 + pt2;

    Having said that, can I ask what compiler you are using? I have tried
    your code in a couple of gcc versions and am not encountering the issue
    you describe:

    18:05:20 Paul Bibbings@JIJOU
    /cygdrive/D/CPPProjects/CLCPP $i686-pc-cygwin-gcc-4.4.3 -std=c++0x
    -c point_decltype.cpp

    18:05:38 Paul Bibbings@JIJOU
    /cygdrive/D/CPPProjects/CLCPP $

    18:05:43 Paul Bibbings@JIJOU
    /cygdrive/D/CPPProjects/CLCPP $i686-pc-cygwin-gcc-4.5.0 -std=c++0x
    -c point_decltype.cpp

    18:06:02 Paul Bibbings@JIJOU
    /cygdrive/D/CPPProjects/CLCPP $

    Having only just obtained the C++0x FCD I'm not that clued up on what
    the correct interpretation of the return type of your op+ should be, not
    having much reading time at the moment. Still, I would be very suprised
    to see how:

    lh = const point<int>&
    rh = const point<double>&

    could lead to:

    decltype(lh.x + rh.x) => const double

    and not just double.

    Have you checked yourself that what irritates you here is expected and
    not simply incorrect? What error messages are you getting from your
    compiler?

    Regards

    Paul Bibbings
    Paul Bibbings, Apr 7, 2010
    #5
  6. Noah Roberts

    Noah Roberts Guest

    In article <>, says...
    >
    > Noah Roberts <> writes:
    >
    > > I'm playing around with C++0x features and have run into something
    > > mildly interesting/irritating:
    > >
    > > template < typename T >
    > > struct point
    > > {
    > > T x,y;
    > > point(T t1, T t2) : x(t1), y(t2) {}
    > > };
    > >
    > > template < typename T1, typename T2 >
    > > auto operator + (point<T1> const& lh, point<T2> const& rh)
    > > -> point< decltype(lh.x + rh.x) >
    > > {
    > > return point< decltype(lh.x + rh.x) >(lh.x + rh.x, lh.y + rh.y);
    > > }
    > >
    > > int main()
    > > {
    > > point<int> pt1(5,5);
    > > point<double> pt2(4.2,6.66);
    > >
    > > point<double> pt3 = pt1 + pt2; // op + returned point<double const>
    > > }
    > >
    > > What is the best way to go about fixing this? I can think of a few ok
    > > solutions but I wonder also if there's something, maybe with rvalue
    > > references, that would solve it elegantly.

    >
    > Given your problem (but see below) it depends upon what you mean by
    > `fix'. One `solution' might be:
    >
    > auto pt3 = pt1 + pt2;
    >
    > Having said that, can I ask what compiler you are using?


    VS 2010 RC

    > Having only just obtained the C++0x FCD I'm not that clued up on what
    > the correct interpretation of the return type of your op+ should be, not
    > having much reading time at the moment. Still, I would be very suprised
    > to see how:
    >
    > lh = const point<int>&
    > rh = const point<double>&
    >
    > could lead to:
    >
    > decltype(lh.x + rh.x) => const double
    >
    > and not just double.
    >
    > Have you checked yourself that what irritates you here is expected and
    > not simply incorrect? What error messages are you getting from your
    > compiler?


    1>e:\dev_workspace\experimental\2010_feature_assessment\2010
    _feature_assessment\main.cpp(148): error C2440: 'initializing' : cannot
    convert from 'point<T>' to 'point<T>'
    1> with
    1> [
    1> T=const double
    1> ]
    1> and
    1> [
    1> T=double
    1> ]
    1> No constructor could take the source type, or constructor
    overload resolution was ambiguous

    line 148 is "point<double> z = x + y;"

    --
    http://crazyeddiecpp.blogspot.com/
    Noah Roberts, Apr 7, 2010
    #6
  7. Noah Roberts <> writes:
    <snip />
    > In article <>, says...
    > ... can I ask what compiler you are using?
    >
    > VS 2010 RC
    >
    >> Having only just obtained the C++0x FCD I'm not that clued up on what
    >> the correct interpretation of the return type of your op+ should be, not
    >> having much reading time at the moment. Still, I would be very suprised
    >> to see how:
    >>
    >> lh = const point<int>&
    >> rh = const point<double>&
    >>
    >> could lead to:
    >>
    >> decltype(lh.x + rh.x) => const double
    >>
    >> and not just double.
    >>
    >> Have you checked yourself that what irritates you here is expected and
    >> not simply incorrect? What error messages are you getting from your
    >> compiler?

    >
    > 1>e:\dev_workspace\experimental\2010_feature_assessment\2010
    > _feature_assessment\main.cpp(148): error C2440: 'initializing' : cannot
    > convert from 'point<T>' to 'point<T>'
    > 1> with
    > 1> [
    > 1> T=const double
    > 1> ]
    > 1> and
    > 1> [
    > 1> T=double
    > 1> ]
    > 1> No constructor could take the source type, or constructor
    > overload resolution was ambiguous
    >
    > line 148 is "point<double> z = x + y;"


    To pursue this further, since we have the case of two implementations
    disagreeing on how your code should behave, I have now had a quick
    browse through the C++0x FCD and found this example, which is not
    a direct counterpart to your example as is, however:

    // ...
    struct A { double x; };
    const A* a = new A();
    // ...
    decltype(a->x) x3; // type is double
    decltype((a->x)) x4 = x3; // type is const double&

    This example is repeated on the MSDN at
    http://msdn.microsoft.com/en-us/library/dd537655(VS.100).aspx.

    Here, A is not a template, so a first point of difference depends on
    whether the above example would lead to the same types if we replaced:

    template<typename T>
    struct A { T x; };
    const A<double>* a = new A<double>();

    A second difference, of course, is that this example uses a pointer to a
    const A and not a reference. Accepting these differences, this example
    indicates that the type of decltype(a->x) does not take on the
    cv-qualification if it's enclosing type, so to speak. That is, *a is
    const A, but that doesn't make decltype(a->x) into const double. I
    might therefore expect a similar handling of decltype(lh.x + rh.x) in
    your code (see OP) for:

    template< typename T1, typename T2 >
    auto operator + (point<T1> const& lh, point<T2> const& rh)
    -> point< decltype(lh.x + rh.x);

    I am of course, as ever, open to be shown the flaws in my thinking.

    What's more puzzling to me, however, is why Comeau rejects your code
    altogether with:

    Comeau C/C++ 4.3.10.1 (Oct 6 2008 11:28:09) for
    ONLINE_EVALUATION_BETA2
    Copyright 1988-2008 Comeau Computing. All rights reserved.
    MODE:strict errors C++ C++0x_extensions

    "ComeauTest.c", line 11: error: "auto" is not allowed here
    auto operator + (point<T1> const& lh, point<T2> const& rh)
    ^

    "ComeauTest.c", line 12: error: expected a ";" (perhaps on the
    previous statement)
    -> point< decltype(lh.x + rh.x) >
    ^

    At end of source: warning: parsing restarts here after previous syntax
    error

    2 errors detected in the compilation of "ComeauTest.c".

    Regards

    Paul Bibbings
    Paul Bibbings, Apr 7, 2010
    #7
  8. Noah Roberts

    Joe Gottman Guest

    Noah Roberts wrote:
    > In article <PtQun.530382$1.easynews.com>,
    > says...
    >> Noah Roberts wrote:


    >> Note that your code fails if T1 or T2 doesn't have a default
    >> constructor. To avoid this problem, C++0x will include a new template
    >> function:
    >> template <class T> T&& declval<T>().

    >
    > Yeah, I noticed this problem but didn't know about declval. Initial
    > attempts to use it didn't fly but I might not be using the right header
    > and/or namespace. Was easy enough to create, since it only needs to be
    > declared, so even if it doesn't exist in this compiler it should work.


    >


    Since declval was only added in the latest meeting of the C++
    standards committee, I don't know if anyone has released an
    implementation yet. But, as you said, it's easy enough to roll your own.

    Joe Gottman
    Joe Gottman, Apr 8, 2010
    #8
    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. Marcin Kaliciñski

    C++0x auto and decltype

    Marcin Kaliciñski, May 9, 2005, in forum: C++
    Replies:
    6
    Views:
    1,848
    Yuriy Solodkyy
    May 11, 2005
  2. Javier
    Replies:
    2
    Views:
    547
    James Kanze
    Sep 4, 2007
  3. George2
    Replies:
    10
    Views:
    580
    Pete Becker
    Dec 17, 2007
  4. Isti
    Replies:
    2
    Views:
    700
  5. Barry
    Replies:
    9
    Views:
    429
    Ara.T.Howard
    Sep 15, 2005
Loading...

Share This Page