There is an undesirable C-style cast that I'm not able to prevent to compile. The undesirable cast performs a C-style cast from an object of some class to a non-const reference of some other class. The classes are unrelated. In the same time I like to support the C-style cast from an object of the same class to the const reference. I'm providing a public conversion operator to support the desirable cast. It seems that it is impossible to prevent the undesirable cast in this case.
The cast to non-const reference fails to build ("Sandbox::B::operator Sandbox::A &()" (declared at line 30) is inaccessible*), unfortunately cast to const reference either fails (error: more than one conversion function from "Sandbox::B" to "const Sandbox::A" applies:
function "Sandbox::B::operator const Sandbox::A &()"
function "Sandbox::B::operator Sandbox::A &()"):
#include <iostream>
#include <string>
#include <cstdlib>
namespace Sandbox {
class A {
public:
A (int i) : _x (i) { }
private:
int _x;
};
class B {
public:
B (const char* m) : _m (m), _a (std::atoi (m)) { }
/*
* This one shall be supported.
*/
operator const A& () {
return _a;
}
private:
/*
* This one shall be not supported.
* If this one is disabled both desired and undesired conversions pass the compilation.
*/
operator A& ();
const std::string _m;
const A _a;
};
}
int main () {
Sandbox::A a (1973);
Sandbox::B b ("1984");
/*
* This is the undesirable cast and it shall fail to compile.
*/
(Sandbox::A&)b;
/*
* This is the desirable cast and it shall pass the compilation.
*/
(const Sandbox::A&)b;
return 0;
}
If I'm disabling operator operator A& ()
both desired and undesired conversions are build.
I'm using gcc, icc and MSVC compiles. I cannot control the client code and prevent there use of C-style cast.
In short: static_cast<>() gives you a compile time checking ability, C-Style cast doesn't. static_cast<>() is more readable and can be spotted easily anywhere inside a C++ source code, C_Style cast is'nt. Intentions are conveyed much better using C++ casts.
Static casts are only available in C++.
C-style typecasts are available in both C and C++, but are considered poor C++ style because they are not as noticeable or precise as the C++ casts. C-style casts can be used to convert any type into any other type, potentially with unsafe results (such as casting an integer into a pointer type).
Static Cast: This is the simplest type of cast which can be used. It is a compile time cast.It does things like implicit conversions between types (such as int to float, or pointer to void*), and it can also call explicit conversion functions (or implicit ones).
This should do the trick (tested on clang3.5):
#include <iostream>
#include <string>
#include <cstdlib>
namespace Sandbox {
class A {
public:
A (int i) : _x (i) { }
void fun()
{
std::cout << "action" << std::endl;
}
private:
int _x;
};
class B {
public:
B (const char* m) : _m (m), _a (std::atoi (m)) { }
/*
* This one shall be supported.
*/
template<typename T, typename Enable = typename std::enable_if<std::is_same<T, A>::value, A>::type>
operator const T& ()
{
return _a;
}
/*
* This one shall be not supported.
* If this one is disabled both desired and undesired conversions pass the compilation.
*/
private:
template<typename T, typename Enable = typename std::enable_if<std::is_same<T, A>::value, A>::type>
operator T& ();
const std::string _m;
const A _a;
};
}
int main () {
Sandbox::A a (1973);
Sandbox::B b ("1984");
/*
* This is the undesirable cast and it shall fail to compile.
*/
(Sandbox::A&)b;
/*
* This is the desirable cast and it shall pass the compilation.
*/
(const Sandbox::A&)b;
return 0;
}
As for why your version doesn't do what you want, it is related to the rules of the C-Style cast:
When the C-style cast expression is encountered, the compiler attempts the following cast expressions, in this order:
a) const_cast(expression)
b) static_cast(expression), with extensions: pointer or reference to a derived class is additionally allowed to be cast to pointer or reference to unambiguous base class (and vice versa) even if the base class is inaccessible (that is, this cast ignores the private inheritance specifier). Same applies to casting pointer to member to pointer to member of unambigous non-virtual base
c) static_cast (with extensions) followed by const_cast
d) reinterpret_cast(expression)
e) reinterpret_cast followed by const_cast
The first choice that satisfies the requirements of the respective cast operator is selected, even if it cannot be compiled
Disclaimer: This explanation is based on guesses mostly, there are multiple steps and complex rules so i'm not sure everything really works as i think i've understood it but here you go.
Since you cast to a reference, reinterpret_cast
will always works based on its rules of type aliasing, so the only way to make that C-Style cast fail is to make a static_cast
on that type unambiguously produce an error. Unfortunately the conversion rules doesn't seem to consider a user defined conversion to const
type to be a better match than a user defined conversion to a non cv-qualified type, they are both on the same level even if the static_cast
target type is const
qualified. Whereas with templates, SFINAE and parameter deduction kicks in and with some magic compiler powder extracted from a mountain dragon, it works. (yeah this step is a little more mysterious to me too).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With