User defined conversion to builtin type gives l-value?

A

Arne Mertz

Hi all, following example:

template <class T, class U>
T foo(U const& u, T const& t)
{
return T temp(u) /= t;
}

gives me an error "'/=' left operand has to be an l-value" on MSVC
2008 express, if T is a builtin type (e.g. double). In my case, U
has an konversion operator to double. If I try the same with T being
a builtin class and either providing a konversion ctor for T or
providing an konversion operator for U, it works.
My question is: is that right according to the standard? I found a
line saying that implicit standard conversions result in l-values,
but as far as i can see this is an explicitly called user defined
conversion or not?

greets
A
 
A

Alf P. Steinbach

* Arne Mertz:
Hi all, following example:

template <class T, class U>
T foo(U const& u, T const& t)
{
return T temp(u) /= t;

This is syntactically incorrect.

Perhaps you meant

return T(u)/t;

}

gives me an error "'/=' left operand has to be an l-value" on MSVC 2008
express, if T is a builtin type (e.g. double). In my case, U has an
konversion operator to double. If I try the same with T being a builtin
class

There are only a few classes that have direct compiler support, they include
typeinfo and some exception classes.

And it's still arguable whether they could be referred to as builtin classes.

Probably you meant "builtin type".

and either providing a konversion ctor for T or providing an
konversion operator for U, it works.

You cannot provide a constructor for a built-in type.

My question is: is that right according to the standard?

Your code should not compile, if that's what you're asking.

I found a line
saying that implicit standard conversions result in l-values, but as far
as i can see this is an explicitly called user defined conversion or not?

Huh?


Cheers & hth.,

- Alf
 
A

Arne Mertz

Alf said:
* Arne Mertz:

This is syntactically incorrect.

Perhaps you meant

return T(u)/t;

Yes, sorry.
There are only a few classes that have direct compiler support, they
include typeinfo and some exception classes.

And it's still arguable whether they could be referred to as builtin
classes.

Probably you meant "builtin type".

Nope, sorry, I meant user defined class.
Your code should not compile, if that's what you're asking.



Huh?

Okay, seems I messed it all up a bit. So another try here:


template <class T, class U>
T foo(U const& u, T const& t)
{
return T(u) /= t;
}

class X {};

struct Y
{
Y() {};
Y(X const&) {}; //konversion ctor userdefined->userdefined (1)
Y(int); //konversion ctor builtin->userdefined (2)
Y& operator /= (Y const& rhs) {}
};

struct Z
{
operator Y() const; //konversion op userdefined->userdefined (3)
operator double() const; //konversion op userdefined->builtin (4)
};

int main()
{
X x; Y y; Z z; int i; double d;

foo(x, y); // calls (1), ok.
foo(i, y); // calls (2), ok.

foo(z, y); // calls (3), ok.
foo(z, i); // calls (4), ERROR, because int(z) is l-value

foo(i, d); // calls double(i) /= d; ERROR because double(i) is
l-value
}

as far as I can the conversion in foo is explicit. I could not find
any phrase in the standard saying whether the result of an explicit
conversion is an l-value or an r-value. as it seems, either the
conversion to userdefined types gives l-values, or the userdefined
op/= can be applied to r-values in contrast to the builtin op/=
Which is the case?

greets
A
 
A

Alf P. Steinbach

* Arne Mertz:
I could not find any
phrase in the standard saying whether the result of an explicit
conversion is an l-value or an r-value.

It's an rvalue.

as it seems, either the
conversion to userdefined types gives l-values, or the userdefined op/=
can be applied to r-values in contrast to the builtin op/=

Yes.

Any accessible non-static member function can be applied to a non-const rvalue
of class type.

Note: that includes a compiler-generated operator=.


Cheers & hth.,

- Alf
 
A

Alf P. Steinbach

* Alf P. Steinbach:
* Arne Mertz:

It's an rvalue.

More precisely (I didn't register that phrasing) the expression is an rvalue.

The result at run time when the expression is evaluated is an object.

Objects are neither lvalues nor rvalues, only expressions are.
 
A

Arne Mertz

Alf said:
* Alf P. Steinbach:

More precisely (I didn't register that phrasing) the expression is an
rvalue.

The result at run time when the expression is evaluated is an object.

Objects are neither lvalues nor rvalues, only expressions are.

Okay, thank you.
In boost::eek:perators this "shorthand" notation is used for one of the
operator-templates, thus preventing those templates to be used with
builtins. Guess I should write a ticket and/or search for a
workaround :(

greets
A
 
S

SG

Alf said:
Perhaps you meant
  return T(u)/t;

Yes, sorry.

[...]

Okay, seems I messed it all up a bit. So another try here:

template <class T, class U>
T foo(U const& u, T const& t)
{
   return T(u) /= t;
}

You either write

return T(u) / t; // suggested previously

or you write

T temp = u;
temp /= t;
return temp;

But "/=" doesn't generally work on rvalues. T(u) *is* an rvalue
expression.

Cheers!
SG
 
A

Arne Mertz

SG said:
You either write

return T(u) / t; // suggested previously

or you write

T temp = u;
temp /= t;
return temp;

Unfortunately, *I* write nothing there. The function is part of the
boost libraries (boost/operators.hpp, line 240). It is only used in
the absence of two #defines, so as a workaround I had to check for
those defines and set one of them explicitly.

Greets
A
 
J

James Kanze

Hi all, following example:
template <class T, class U>
T foo(U const& u, T const& t)
{
return T temp(u) /= t;
}
gives me an error "'/=' left operand has to be an l-value" on
MSVC 2008 express, if T is a builtin type (e.g. double). In my
case, U has an konversion operator to double. If I try the
same with T being a builtin class and either providing a
konversion ctor for T or providing an konversion operator for
U, it works. My question is: is that right according to the
standard? I found a line saying that implicit standard
conversions result in l-values, but as far as i can see this
is an explicitly called user defined conversion or not?

Alf's already pointed out a couple of errors in your
formulation: illegal syntax in the example, constructors for a
build in type. Those aren't nits: copy paste the actual code
for code, and as for the others: being able to express yourself
clearly, concisely and precisely in your native language is a
prerequisite to learning any programming language. (I realize
that English probably isn't your native language, judging from
your return address, but in this case, the translation would be
literal to and from any other language: "Es gibt keinen
Constructor für eingebaut Type" in German, as well. And if you
really do have problems with English, the people in
de.comp.lang.iso-c++ are very friendly as well.)

Anyway: I don't know where you found anything saying that
implicit standard conversions result in lvalues. The only
conversions which result in lvalues are those to references, and
none of the standard conversions result in a references. The
only way something like "return T(u) /= t ;" could be legal is
if T had a user defined conversion operator which returned a
reference, or if operator/=() was a member function of T (or a
base class of T). (The latter is actually a frequent case.)
 
J

James Kanze

SG schrieb:
Unfortunately, *I* write nothing there. The function is part
of the boost libraries (boost/operators.hpp, line 240). It is
only used in the absence of two #defines, so as a workaround I
had to check for those defines and set one of them explicitly.

Have you actually read the documentation of Boost::eek:perators?
They provide operators for user defined classes. Not in the
best way, of course---you really want to use the Barton and
Nackman trick here, and provide them as friends of a base
class. But that's not the point here: if you instantiate e.g.
operator/ with X as the first template argument, then it is a
pre-condition that X has a member function operator/=.
Otherwise, you're not using the library as specified.

Of course, I can't find any way you could reasonably use the
library as it is specified---you need operator/ to be somewhere
where ADL will find it, and ADL will not look into namespace
boost. (That's why the Barton and Nackman trick is preferred.)
And of course, there really isn't any good reason to not support
classes with operator/= as a free function---arguably, it should
be a free function, because that is the only way to make it
require an lvalue. (On the other hand, operator= can't be a
free function, so you've lost this aspect anyway, and you might
as well go ahead and make them all members. I know I usuallly
do.)
 
A

Arne Mertz

James said:
Have you actually read the documentation of Boost::eek:perators?
They provide operators for user defined classes. Not in the
best way, of course---you really want to use the Barton and
Nackman trick here, and provide them as friends of a base
class. But that's not the point here: if you instantiate e.g.
operator/ with X as the first template argument, then it is a
pre-condition that X has a member function operator/=.
Otherwise, you're not using the library as specified.

Well, as far as I can see the documentation says only that the
operation has to compile, not that operator /= must be a member.
Of course, I can't find any way you could reasonably use the
library as it is specified---you need operator/ to be somewhere
where ADL will find it, and ADL will not look into namespace
boost. (That's why the Barton and Nackman trick is preferred.)
And of course, there really isn't any good reason to not support
classes with operator/= as a free function---arguably, it should
be a free function, because that is the only way to make it
require an lvalue.

In my case it is indeed a free function - it is the builtin
operator/=(double, double). The problem occurs with
dividable2_left<double, X>, which implements

double operator-(X const&, double const&) by converting the X into
double and applying /=

I know that the mixed arithmetic templates <T,U> should be declared
base classes of T, not of U, but as T is double I derive U from
those templates to make the lookup possible. As I said, the short
form is only used when BOOST_HAS_NRVO and
BOOST_FORCE_SYMMETRIC_OPERATORS are not #defined so at the moment I
make sure that one of the two is defined:

#include <boost/config.hpp>
#ifndef BOOST_HAS_NRVO
#define BOOST_FORCE_SYMMETRIC_OPERATORS
#endif

#include <boost/operators.hpp>

But what puzzles me is that if the left template argument is a
userdefined type with a free operator/=, the problem does not occur:

class Y {};

Y& operator/= (Y& lhs, Y const&) { return lhs; }


struct X : public boost::dividable2_left<Y, X>
{
operator Y() const {return Y();}
};

//injected by boost:
//Y boost::eek:perator/ (Y vonst& lhs, X const& rhs)
//{ return X(lhs) /= rhs; }
//

int main()
{
X x;
Y y;
Y e = x / y;
}

greets
A
 
J

James Kanze

Well, as far as I can see the documentation says only that the
operation has to compile, not that operator /= must be a
member.

That's true in the tables, but the introductory documentation
does make a point of them being member functions. (Of course,
logically, it should be documented as a constraint.)
In my case it is indeed a free function - it is the builtin
operator/=(double, double). The problem occurs with
dividable2_left<double, X>, which implements
double operator-(X const&, double const&) by converting the X
into double and applying /=

I'm not sure I understand. You can't overload /= for double,
double. The compiler won't even consider user defined overloads
in such cases.
I know that the mixed arithmetic templates <T,U> should be
declared base classes of T, not of U, but as T is double I
derive U from those templates to make the lookup possible. As
I said, the short form is only used when BOOST_HAS_NRVO and
BOOST_FORCE_SYMMETRIC_OPERATORS are not #defined so at the
moment I make sure that one of the two is defined:

The source is such a confused mess of conditional compilations
that I can't make heads or tails of it. What I think is that
you have two variants (after expansion of all the macros):

T
operator/( T lhs, U const& rhs )
{
return lhs /= rhs ;
}

and

T
operator/( T const& lhs, U const& rhs )
{
T nrv( lhs ) ;
nrv /= rhs ;
return nrv ;
}

Both of these should work with T == double. (The first isn't
what I'd consider good coding style, but apparently, it's an
optimization for compilers which don't support NRVO.)

The case you're interested in is more complex, however, since
they do an implicit conversion on the value to the left of the
/=. There's no way this can work for a built-in type.
#include <boost/config.hpp>
#ifndef BOOST_HAS_NRVO
#define BOOST_FORCE_SYMMETRIC_OPERATORS
#endif
#include <boost/operators.hpp>

I think that BOOST_FORCE_SYMMETRIC_OPERATORS is designed to be
set by you. Just define it, before including any Boost headers,
and I think you should be OK. (I can't test it, because for my
compiler here, BOOST_HAS_NRVO is set, systematically, so unless
I use a hack like the above to undefine it, your code works.)
But what puzzles me is that if the left template argument is a
user defined type with a free operator/=, the problem does not
occur:
class Y {};
Y& operator/= (Y& lhs, Y const&) { return lhs; }
struct X : public boost::dividable2_left<Y, X>
{
operator Y() const {return Y();}
};
//injected by boost:
//Y boost::eek:perator/ (Y vonst& lhs, X const& rhs)
//{ return X(lhs) /= rhs; }
//
int main()
{
X x;
Y y;
Y e = x / y;

}

Yes it does.

In the beginning of the thread, you mentioned VC++, I think.
I'm not sure, but I seem to recall hearing that VC++ doesn't
enforce the rule about not binding a temporary to a non-const
reference. In which case, the above will work (but the built-in
operator won't, because the rules for lvalues are enforced).
 
A

Arne Mertz

James said:
I'm not sure I understand. You can't overload /= for double,
double. The compiler won't even consider user defined overloads
in such cases.

Actually I don't try to overload operator/= for double, double. All
The source is such a confused mess of conditional compilations
that I can't make heads or tails of it. What I think is that
you have two variants (after expansion of all the macros):

T
operator/( T lhs, U const& rhs )
{
return lhs /= rhs ;
}

and

T
operator/( T const& lhs, U const& rhs )
{
T nrv( lhs ) ;
nrv /= rhs ;
return nrv ;
}
these are not the implementations of the operator I have problems
with. its not T op/(T, U), but T op/(U, T).
The two versions of the BOOST_OPERATOR2_LEFT macros are

#if defined(BOOST_HAS_NRVO) || \
defined(BOOST_FORCE_SYMMETRIC_OPERATORS)

friend T operator OP( const U& lhs, const T& rhs )
{ T nrv( lhs ); nrv OP##= rhs; return nrv; }


#else

friend T operator OP( const U& lhs, const T& rhs )
{ return T( lhs ) OP##= rhs; }

#endif

the latter is the version I am having problems with, if T is double.
Both of these should work with T == double. (The first isn't
what I'd consider good coding style, but apparently, it's an
optimization for compilers which don't support NRVO.)

> The case you're interested in is more complex, however, since
> they do an implicit conversion on the value to the left of the
> /=. There's no way this can work for a built-in type.
>

Yes, for the op(T,U) operators thats OK, but for the op(U,T)
apparently it is not, because the U has to be converted into T,
resulting in the problems I described. And note the comment to the
non-nrvo version:

// [...] Note that the implementation of
// BOOST_OPERATOR2_LEFT(NAME) only looks cool, but doesn't provide
// optimization opportunities to the compiler :)

[snip]
Yes it does.

In the beginning of the thread, you mentioned VC++, I think.
I'm not sure, but I seem to recall hearing that VC++ doesn't
enforce the rule about not binding a temporary to a non-const
reference. In which case, the above will work (but the built-in
operator won't, because the rules for lvalues are enforced).

Ah okay, so it's a non-conformity to the standard that makes it work
on MSVC?

greets
A
 

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

No members online now.

Forum statistics

Threads
473,954
Messages
2,570,116
Members
46,704
Latest member
BernadineF

Latest Threads

Top