这是一个小系统,可以将变量智能转换为一系列类型
Ts...
,使得列表中第一个元素
Ts...
是隐式转换为变量所选择的类型:
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) );
}
现在,我们可以按照以下方式修改
foo
的想法:
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)) );
}
foo
会将A
尽可能地转换为X
,如果不能转换则转换为Y
。
我们可以编写一个整个系统,通过在types< types<X,Y> >
这样的包中传递foo
的重载描述和一个重载集合,将其传递给一些神奇的代码,该代码将生成一个调度程序,但那是过度设计。
实时示例
这使用了C++11功能。在C++14中,我们可以在smart_cast
中省略一些冗长的措辞。
设计非常简单。 我们创建一个types
bundle以处理类型的打包(只是样板文件)。
然后我们的details::smart_cast_t
具有回退基础专业化,仅将我们的U
转换为U
。 如果我们可以将U
转换为第一个类型,则这样做。 否则,我们在父类型上进行递归,可能在基础专业化中终止。
我会隐藏细节,使我们的公共函数变得简单--它是
smart_cast<type1, type2, type3, etc>(expression)
。我们不必传递
U
表达式的类型,因为我们推断出来,然后将其传递到详细信息中进行处理。
在运行时,这将减少为单个隐式转换:所有上述内容都是在编译时完成的。
唯一的缺点是,这可能会导致某些编译器发出警告,因为我们使用了隐式转换。在
smart_cast_t
的第一个非基本特化中添加
static_cast<T0>
以避免这种情况。
我从
true_type
和
false_type
无意中继承了
smart_cast_t
。这意味着
smart_cast_t<U,types<Ts...>>::value
的值将告诉您是否将
U
转换为任何
Ts...
,或者只是保留为
U
。
调用方负责rvalue和lvalue类别。如果转换失败,则返回U而不是U&&(如果传递了rvalue)。我认为回退到U会生成更好的错误消息,但是如果您希望smart_cast<int, double>(std::string("hello"))无法编译而不是返回std::string,则只需从smart_cast_t的基本专业化中删除operator()即可。
顺便说一下,对于smart_cast<int, double>(""),我也不能正常工作。可能需要在那里添加一些typename std::decay<U>::type之类的东西。
foo
来实现吗? - Yakk - Adam Nevraumontfoo
(并非总是如此),我总是更喜欢使用foo(X)
而不是foo(Y)
。如果只有一个foo(Y)
,那么我想要转换为Y
。如果只有一个foo(X)
,那么当然要转换为X
。 - gexicideZ
吗?如果是这样,那么你就没什么运气了。如果我可以在其他点上做些事情,我就可以解决它。但需要从 gexicide 那里得到澄清。 - Yakk - Adam Nevraumontfoo(z)
的代码可能不会硬编码转换(例如,不能简单地将static_cast
转换为X
),因为代码应独立于Z
可以转换的类型。也就是说,调用foo(z)
的代码既不能提到X
也不能提到Y
。 - gexicide