std::is_convertible 判断类型转换
C++ 11知识点:std::is_convertible
简介
is_convertible用于判断是否可以转换:第一个模板参数的类型是否可以转换为第一个模板参数的类型。
如果To, From都是基本类型,那么可以隐式转换。
如果To是基类,From是子类,那么From可以转换为To。
class template 声明
template<typename From, typename To>
struct is_convertible;
is_convertible 继承自integral_constant,如果能转换,可以从is_convertible<>::value萃取到true;如果不能转换,则萃取到false。
可能实现
如何实现std::is_convertible, 判断:类型From是否能转化成To呢?
cppreference提供了一种实现方式:使用一组测试函数test_xxx。
其中,
- test_returnable
() 用于判断To是否可以作为函数返回值类型,用std::true_type表示可以,std::false_type表示不能,通过::value分别得到结果(true/false)。如果可以(true),编译器选择第一个版本(形参为int类型);如果不行,就选择第二个版本(形参为...)。 - test_implicitly_convertible<From, To>() 用于判断From是否能隐式转换为To,结果也用std::true_type/std::false_type表示。如果可以(true),编译器选择第一个版本;如果不能(false),就选择第二个版本。
std::is_convertible一种可能实现(from en.cppreference.com):
namespace detail {
// test_returnable 判断T类型是否可以作为函数返回值
// 第一个版本 通过::value得到true
template<class T>
auto test_returnable(int) -> decltype(
void(static_cast<T(*)()>(nullptr)), std::true_type{}
);
// 第二个版本 通过::value得到false
template<class>
auto test_returnable(...) -> std::false_type;
// test_implicitly_convertible 判断From是否能隐私转换为To
// 第一个版本 通过::value得到true
template<class From, class To>
auto test_implicitly_convertible(int) -> decltype(
// 看起来这里是调用了std::declval<From>(), 但实际上只是利用它得到类型From&&
// 函数实参类型From会隐式转换成形参类型To, 如果能转换, 则匹配;如果不能转换,则不匹配
void(std::declval<void(&)(To)>()(std::declval<From>())), std::true_type{}
);
// 第二个版本 通过::value得到false
template<class, class>
auto test_implicitly_convertible(...) -> std::false_type;
} // namespace detail
// is_convertible 判断From是否能转化为To
template<class From, class To>
struct is_convertible : std::integral_constant<bool,
// test_returnable<>::value为true, 确保To可作为函数返回类型, 因为后面的test_implicitly_convertible用到了std::declval()将To作为返回类型
(decltype(detail::test_returnable<To>(0))::value &&
decltype(detail::test_implicitly_convertible<From, To>(0))::value) ||
(std::is_void<From>::value && std::is_void<To>::value)
> {};
is_convertible<From, To> 判断From是否能转换为To,分为这几种情况(逻辑或):
1)From,To都是void类型,::value返回true,表示可以;
2)From能隐式转换为To。
第1)点很简单,通过std::is_void<>对From、To进行判断即可。
第2)点用到了函数模板test_implicitly_convertible,前提是To能作为返回值类型,因此需要用test_returnable
下面重点分析test_implicitly_convertible的实现。
// 当From能隐式转换为To时,调用该函数模板;否则,发生不匹配,调用另一个重载的函数模板
template<class From, class To>
auto test_implicitly_convertible(int) -> decltype(
void(std::declval<void(&)(To)>()(std::declval<From>())), std::true_type{}
);
void(&)(To)是一个函数引用类型:返回值void,参数To类型,我们记为Fun;
为了方便,我们先将std::declval
void(std::declval<void(&)(To)>()(std::declval<From>()))
// 可以理解成:
void(Fun(From))
Fun是void(&)(To),这是一个函数引用类型,当调用Fun(From)时,实参类型From会隐式转换成形参类型To。如果能隐式转换,则会匹配test_implicitly_convertible第一个版本;否则,匹配第二个版本。
实际上,std::declval
std::declval可能实现:
template<typename T> constexpr bool always_false = false;
// 类型推导能得到T&&类型, 即为T添加右值引用
// 禁止调用declval<T>()构造对象, 只能(结合decltype)用于类型推导
template<typename T>
typename std::add_rvalue_reference<T>::type declval() noexcept {
static_assert(always_false<T>, "declval not allowed in an evaluated context");
}
函数模板std::declval不允许用来调用以构造对象,需要用结合deltype来进行类型推导。
struct Default { int foo() const { return 1; }};
// 错误使用
std::declval<Default>().foo(); // 错误原因:不能调用declval<>()构造对象
// 正确使用
decltype(std::declval<Default>().foo()) n = 2; // OK, n为int类型, 值2
示例
A是B基类,B可以转换为A,反之不行。
// is_convertible example#include <iostream>#include <type_traits>
struct A { };
struct B : A { };
int main() {
std::cout << std::boolalpha; /* 输出流将bool输出true/false, 而不是1/0 */
std::cout << "is_convertible:" << std::endl;
std::cout << "int => float: " << std::is_convertible<int,float>::value << std::endl;
std::cout << "int = >const int: " << std::is_convertible<int,const int>::value << std::endl;
std::cout << "A => B: " << std::is_convertible<A,B>::value << std::endl; /* A不能转换为B */
std::cout << "B => A: " << std::is_convertible<B,A>::value << std::endl; /* B能转换为A */
return 0;
}
输出
is_convertible:
int => float: true
int => const int: true
A => B: false
B => A: true
参考
std::is_convertible | cplusplus
std::is_convertible | cppreference