Why do the following two usecases behave differently?
An example for a built-in type:
class Test
{
operator const char*() {
return "operator const char*() called";
}
operator char*() {
return const_cast<char*> ("operator char*() called");
// a really ugly hack for demonstration purposes
}
};
Test test;
const char *c_ch = test; // -> operator const char*() called
char *ch = test; // -> operator char*() called
Okay, everything works as it should. No, let’s try it with a user-defined type:
class ColorWrapper
{
operator const Color() {
cout << "operator const Color() called" << endl;
return Color(10, 20, 30);
}
operator Color() {
cout << "operator Color() called" << endl;
return Color(11, 22, 33);
}
};
ColorWrapper cw;
const Color c_col = cw;
Color col = cw;
The situation looks identical; but now, GCC starts complaining:
error: conversion from 'ColorWrapper' to 'const Color' is ambiguous
error: conversion from 'ColorWrapper' to 'Color' is ambiguous
Am I missing something? What I’m trying to achieve here is to prevent the user from changing the color directly. If they want to change it, they must do it via the wrapper class.
The thing is, that your two examples are not equivalent. The first returns two different types from which the compiler can easily choose, a pointer to mutable data and a pointer to constant data. It would be different, was it a pointer vs a constant pointer (which would mean you cannot change where the returned pointer points, in contrast to not changing the pointed to data):
which would be equivalent to your second example and introduce the same ambiguity, since the compiler cannot decide which one to choose, as both return the same type, one const-qualified and the other not.
This could be solved by overloading based on the constness of the object:
But this won’t buy you anything here, since you return a new object by value anyway, so you can always return the const version, and also the appropriate version would be chosen based on the source object’s constness and not the destination obejct’s constness (thus call the non-const version in both cases), which doesn’t seem what you want.
So you can overload based on the constness of the source object. But you cannot, as you want to now, overload simply based on the constness of the destination object.
This all reduces down to thinking about the difference between an object and the data the object may inderectly refer to, which in practice presents itself as the difference between a constant pointer and a pointer to constant data.
Then there’s absolutely no need for any overloading. Just return the const version. The user cannot change the returned color directly, he can only copy it and change the copy.