20

C++ allows for overloading type casts by creating an operator T() where T is the type we want to cast to.

Now, how does this feature play together with references? For example:

struct Y{ int i; }; struct X{ Y y; operator Y() const { return y; } }; 

Here, we can cast an X to Y which will simply return the contained Y. But what if we want to make a cast to an Y reference. For example, C++ allows us to do this:

struct X{ Y y; operator Y&(){ return y; } operator const Y&() const { return y; } }; 

Now, we can cast an X to a Y reference or a const reference (which also works for a const X).

Is there any difference in the semantics of the first and the second example? What is the best way if I want to allow casting to a reference?

I could imagine that even if I write operator T() without any &, C++ might allow that the cast returns a result by reference (i.e., it might somehow add implicit operator T&() methods when I specify operator T()).

For example, I want the following code to work:

int main(){ X x; Y& y = x; // y now references the y inside x y.i = 5; std::cout << x.y.i << std::endl; // Should print 5 now } 

What is the most simple way to achieve this?

3
  • 4
    Are you asking about the semantic difference between returning a value and a reference, or between returning const and non-const references? Commented Aug 14, 2014 at 8:40
  • @Useless: I am asking: Does declaring a operator T() (without &) imply that the cast always returns a T by value? Or are there some rules that T may be returned as reference in some cases? I have tried to clarify the question. Commented Aug 14, 2014 at 8:43
  • Be aware of the implication of returning a reference: it forces the class to have a data member of the target type. In contrast, returning a value allows the class to be implemented with any data members, as long as they can provide the necessary information to create an object of the target type. Commented Apr 26 at 10:18

3 Answers 3

10

Does declaring a operator T() imply that the cast always returns a T by value?

Yes.

The return type of conversion operators is implicitly exactly what they convert to.

[...] the type of the conversion function is “function taking no parameter returning conversion-type-id”.

§12.3.2 [class.conv.fct]

Sign up to request clarification or add additional context in comments.

12 Comments

Thanks! But if I declare the lower example (with T& and const T&), then can I leave out the T cast, because reference + const reference can always be used?
Yes, because: "Conversion functions that return “reference to cv2 X” return lvalues or xvalues, depending on the type of reference, of type “cv2 X” and are therefore considered to yield X for this process of selecting candidate functions." §13.3.1.5 [over.match.conv]
@gexicide: if providing by-reference versions, you almost certainly want to leave out the T versions, as you'll otherwise need client code to manually disambiguate a lot of client usage (i.e. to explicitly code either x.operator Y() or x.operator Y&()).
If C++11, rvalue reference to this overload that returns Y is wise @tonyd
@Yakk: sometimes - other times the Y returned may be used immediately as part of an expression, and won't have any reference bound to it, or may be referred to for calculation purposes but not copied from....
|
7

Does declaring a operator T() imply that the cast always returns a T by value?

It means that the conversion operator will return as specified by T. Be that a reference, pointer or value. For example, given some type (as per the OP) Y:

operator Y(); operator Y&(); operator Y*(); 

Are all (with the cv-qualified variations) valid user defined conversions. To use Y as a reference, you will need to return it as a reference.

So, yes, casting to a T that is not a reference means that the return type is a value. This is inline with the general casting semantics - a value is returned.

Is there any difference in the semantics of the first and the second example?

Yes there are semantic differences. They would be the same semantic deferences as in other methods with reference vs. value return types. They would be different in the same way as the following two methods are different;

Y get_y_value(); Y& get_y_ref(); 

Notes

Mixing operator Y(); and operator Y&(); in the same class could lead to ambiguous overloads (at least clang complains about this, but gcc seems happy to mix the two).

I'm not going to opinion on which one is better here as the details of your situation are unclear. In general though, when implementing a user defined conversion, always consider how casts are typically performed and that rvalues are probably returned more often than not. With the advent of C++11, favour them being explicit as well; for the same reasons constructors are explicit (or not if specifically required).

2 Comments

I think you don't need operator Y() if you already have operator Y&(). Maybe unless you have different implementation inside operator Y().
That’s right, but both forms are available. I haven’t checked compliance of late, but as noted you may run into ambiguity issues as well
5

With clang++:

#include <iostream> class test { public: test(int n) : nb{n} {} public: operator int() { return nb; } private: int nb; }; int main(void) { test t{42}; int x = t; int& ref = t; // error: non-const lvalue reference to type 'int' cannot bind to a value of unrelated type 'test' int&& rref = t; // compiles fine std::cout << x << std::endl; } 

Which suggest that you do return a temporary new value.

The real question to help you choose between your two cases is: do you want to let people modify your internal member (return a reference) and is that member expensive to copy (use a const reference instead of value) ?

5 Comments

Yes, I want to let people modify the member. However, I don't know if it is an expensive copy, as its type is a template parameter, so it can depend. I simply want to give clients the highest flexibility. What would be the best set of cast operators then?
("intx" should be "int x".. apart that) no one mentionin is not very good style implicit cast operator overloading? What about just providing template setter and getters (with one overload for value copying, one for l-reference and one for r-reference)
@gexicide Your second choice, reference and const reference conversion marked as const.
@DarioOO Well it depends what you want to achieve, cast operator have a stronger semantic than a simple getter/setter, it basically means that your types are compatible enough so one can convert into the other. Implicit conversion makes a lot of sense in some cases although it can become a mess if used in a wrong way, but hey that's true for all c++. Thx for the typo, it's fixed :)
Add operator int&() { return nb; } and int& ref = t; works as well.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.