decltype and const reference params

N

Noah Roberts

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

Noah Roberts

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

Joe Gottman

Noah said:
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
 
N

Noah Roberts

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

Paul Bibbings

Noah Roberts said:
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
 
N

Noah Roberts

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;"
 
P

Paul Bibbings

Noah Roberts said:
... 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
 
J

Joe Gottman

Noah said:
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
 

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

Members online

Forum statistics

Threads
473,764
Messages
2,569,566
Members
45,041
Latest member
RomeoFarnh

Latest Threads

Top