GMan has posted a code for the delicious auto_cast
“operator” that allows to write code such as the following in C++:
float f = 4.0f;
int i = auto_cast(f);
// instead of:
int j = static_cast<int>(f);
or, more saliently,
T x = value;
typename nested_type<with, template_arguments>::type y = auto_cast(x);
// instead of
typedef typename nested_type<with, template_arguments>::type my_type;
my_type z = static_cast<my_type>(x);
Basically, the operator is great in removing unnecessary redundancy from a static_cast
, and at the same time is still safe. It’s even more safe than static_cast
since it prevents accidentally mismatching the types:
int i = 1234;
short s = static_cast<char>(i); // s == -46, not 1234!
However, j_random_hacker has noticed a flaw in the operator:
static_cast allows downcasts, which are potentially unsafe.
Indeed, an auto_cast
should probably forbid downcasts because they may fail:
class base { };
class derived : public base { };
base b;
derived* pd = auto_cast(&b); // should fail at compile time.
Hence my question:
How would you modify the auto_cast
implementation in order to forbid downcasts?
This will probably involve enable_if
. I’m especially interested in a solution that allows the compiler to provide good diagnostics in case of failure (= readable error messages).
It appears you want to use the T{u}
form of initialization.
template <typename U>
operator U()
{
return U{std::forward<T>(mX)};
}
One of the reasons for these uniform initialization was that to use explicit constructors for creating a temporary, you need a cast aka T(u)
. With T{u}
that problem was solved. For C++03, I imagine you could do something like this:
template<typename T>
struct construct_explicit {
template<typename U>
construct_explicit(U &u):t(u) { }
template<typename U>
construct_explicit(U const &u):t(u) { }
T &get() { return t; }
T const& get() const { return t; }
T t;
};
Then you can say construct_explicit<U>(mX).get()
, although in a case like in your conversion function, it also works to use a named variable as an intermediary step, I think
template <typename U>
operator U()
{
// or C++03: U u(mX);
U u(std::forward<T>(mX));
return u;
}
You could use type-traits to disable the operator if T
is a base of R
. As we're in C++0x, you can explicitly static_assert(std::is_base_of<T, U>::value, "Cannot auto_cast downwards!");
I wouldn't even use auto_cast because static_cast, const_cast, dynamic_cast and reinterpret_cast are also made ugly by design to help pointing code that could need refactoring : a ugly operation should have an ugly look.
A secondary reason for introducing the
new-style cast was that C-style casts
are very hard to spot in a program.
For example, you can't conveniently
search for casts using an ordinary
editor or word processor. This
near-invisibility of C-style casts is
especially unfortunate because they
are so potentially damaging. An ugly
operation should have an ugly
syntactic form. That observation was
part of the reason for choosing the
syntax for the new-style casts. A
further reason was for the new-style
casts to match the template notation,
so that programmers can write their
own casts, especially run-time checked
casts.
http://www2.research.att.com/~bs/bs_faq2.html#static-cast
I prefer to see clearly in the code where it could be better, or where we explicitely need to make this ugly operation.