Here is a little system that smart casts a variable to to a sequence of types Ts...
such that the first element in the list Ts...
that the variable implicitly converts to is the one chosen:
namespace details {
template<class...>struct types{using type=types;};
template<class U, class Types, class=void>
struct smart_cast_t:std::false_type {
using type=U;
template<class A>
U operator()(A&& a)const{return std::forward<A>(a);}
};
template<class U, class T0, class...Ts>
struct smart_cast_t<
U, types<T0, Ts...>,
typename std::enable_if<std::is_convertible<U, T0>::value>::type
>:std::true_type
{
using type=T0;
template<class A>
T0 operator()(A&& a)const{return std::forward<A>(a);}
};
template<class U, class T0, class...Ts>
struct smart_cast_t<
U, types<T0, Ts...>,
typename std::enable_if<!std::is_convertible<U, T0>::value>::type
>:smart_cast_t< U, types<Ts...> >
{};
}
template<class... Ts, class U>
auto smart_cast( U&& u )
-> decltype(details::smart_cast_t< U, details::types<Ts...> >{}( std::forward<U>(u) ))
{
return details::smart_cast_t< U, details::types<Ts...> >{}( std::forward<U>(u) );
}
Now, the idea is that we can now modify foo
as follows:
void foo_impl(X);
void foo_impl(Y);
template<class A>
void foo(A&& a) {
foo_impl( smart_cast<X, Y>(std::forward<A>(a)) );
}
and foo
casts the A
to X
if possible, and if not to Y
.
We could write up an entire system whereby we pass in a description of the overloads of foo
in a package like types< types<X,Y> >
and an overload set of foo
to some magic code, which spits out a dispatcher, but that would be over engineering.
live example
This uses C++11 features. With C++14 we get to drop some verbage in smart_cast
.
The design is pretty simple. We create a types
bundle to work with bundles of types (just boilerplate).
Then our details::smart_cast_t
has a fallback base specialization that just converts our U
to U
. If we can convert U
to the first type, we do so. Otherwise, we recurse on our parent type, possibly terminating in the base specialization.
I hide that in details, so our public function is simple -- it is smart_cast< type1, type2, type3, etc >( expression )
. We don't have to pass U
the type of the expression, as we deduce it, then pass it into details for the work to be done.
At run time this reduces to a single implicit cast: all the above is what is done at compile time.
About the only downside is that this can result in warnings on some compilers, as we use implicit conversion. Add a static_cast<T0>
to the first non-base specialization of smart_cast_t
to avoid this.
I inherited smart_cast_t
from true_type
and false_type
needlessly. This means that the value of smart_cast_t<U, types<Ts...>>::value
will tell you if U
converted to any of Ts...
, or was just left alone as a U
.
The caller is responsible for rvalue vs lvalue categories. If the cast fails, it will return a U
not a U&&
if passed an rvalue. The fallback -- to U
-- I think will generate better error messages, but if you prefer that smart_cast<int, double>(std::string("hello"))
fail to compile instead of returning a std::string
, simply remove the operator()
from the base specialization of smart_cast_t
.
Speaking of which, I also don't work right for smart_cast<int, double>("")
. Might need some typename std::decay<U>::type
s or something in there.