Just a curiosity (academic question :P), consider the following code:
struct Y {};
class PassConstY {
public:
PassConstY(const Y& y) {}
};
class PassY {
public:
PassY(Y& y) {}
};
void z(PassConstY y) {}
void z(PassY y) {}
void h(const Y& y) {}
void h(Y& y) {}
Y y;
const Y cy;
h(cy); //OK
h(y); //OK
z(cy); //OK
z(y); //Ambiguity
Question: is it possible to write PassY
and PassConstY
st
z
and h
obeys exactly the same overloading rules z
and h
definitions for the mutable Y
the code still compiles (ie I can call z
and h
with the mutable version too). My guess is no, in the sense that I managed to have PassConstY
constructable from a const Y
only (and not a mutable Y
), which removes the ambiguity, but then point 2 is doomed to failure.
Clarifications:
PassY and PassConstY can be defined as you prefer (but must be classes, may be templated) but the definitions of z and h must be unchanged.
The following code must compile when only the "const" versions of z and h are defined:
const Y y;
z(y); //Calls z(PassConstY y)
h(y); //Calls h(const Y& y)
Y x;
z(x); //Calls z(PassConstY y)
h(x); //Calls h(const Y& y)
The following code must compile when only the "mutable" versions of z and h are defined:
Y x;
z(x); //Calls z(PassY y)
h(x); //Calls h(Y& y)
And the following code must compile when both versions of h and z are defined:
const Y y;
z(y); //Calls z(PassConstY y)
h(y); //Calls h(const Y& y)
Y x;
z(x); //Calls z(PassY y)
h(x); //Calls h(Y& y)
Sorry if the original question was not clear enough!
Motivations (really another question of mine here on Stackoverflow):
Regaring the "disable r-value binding" in the comment below, I would like to come up with a template class Ref (think of it like a smart reference) that I can use when I write function definitions like:
struct X {};
void f(Ref<X> x) {} //Instead of void f(X& x)
void f(Ref<const X> x) {} //Instead of void f(const X& x)
so that I get the same behaviour when calling the function f (in terms of overloading resolution/conversions) as for the plain reference versions, but Ref never binds to rvalues.
[For this part, C++0X is needed]
The reason of this is:
For h
, which uses a SCS, the first bullet will figure out a winner for the case where both functions are viable candidates (second call). For z
, the case where both functions are viable candidates (second call) there is no UCS better than the other UCS, because both UCSs use different constructors.
Notice that an UCS is defined as
TypeOf(*this) &
for a conversion operator function) You cannot formate a SCS for "const Y to Y&", so there exist no first SCS for the UCS for the second h
function, and so it is not a viable candidate. This is not true for the second call to h
though, and it thus results in the ambiguity described above.
I've shortened some terms:
UCS : User-defined Conversion Sequence
SCS : Standard Conversion Sequence
My guess is no, in the sense that I managed to have PassConstY constructable from a const Y only (and not a mutable Y), which removes the ambiguity, but then point 2 is doomed to failure.
That suspicion of yours, if I understand you correctly, is not true. With your current definitions, you can remove the mutable versions of both h
and z
and your code will compile fine. There is an SCS for Y
to const Y&
.
You can, of course, rewrite PassY and PassConstY as
typedef Y &PassY;
typedef Y const& PassConstY;
Other than that, I'm not sure what you are heading at when you say "rewrite". If you want to just change the class body, then definitely overload resolution will be different.
I'm not sure if this may help you but you could write wrapper for z
that uses templates:
template <typename T>
struct TypeFor {
typedef PassY type;
};
template <typename T>
struct TypeFor<const T> {
typedef PassConstY type;
};
template <typename T>
void wrap_z(T& y) { z(static_cast<typename TypeFor<T>::type>(y)); }
Now depending on your calling argument type, T
will become either Y
or const Y
and the correct explicit conversion will be used.
This uses the fact that structs (but not functions) can be partially specialized, which we have done here for different constness of T
.
z(y) is ambiguous because there is an implicit conversion in between and it does not know which one.
If you used explicit constructors to PassY and PassConstY then both z's would fail.
You could also make Pass a template with an implicit constructor:
template< typename T >
class Pass
{
Pass( T& t ) {}
};
typedef Pass<Y> PassY;
typedef Pass<const Y> PassConstY;
I am still not sure this would solve your problem though but you could also make za template function:
template< typename T> void z( Pass<T> t );
although in real code I would avoid the implicit conversion and use a function
template<typename T> Pass<T> pass(T& t) { return Pass<T>(t); }
Now above is a partial-specialisation and you can also define
template< typename T> void z( T& t) { z( pass(t) ); }
and it should work.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.