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_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()简单理解成T。这样,std::declval<void(&)(To)>(),相当于Fun;std::declval() 相当于From。

void(std::declval<void(&)(To)>()(std::declval<From>())) 
// 可以理解成:
void(Fun(From))

Fun是void(&)(To),这是一个函数引用类型,当调用Fun(From)时,实参类型From会隐式转换成形参类型To。如果能隐式转换,则会匹配test_implicitly_convertible第一个版本;否则,匹配第二个版本。

实际上,std::declval是为T添加右值引用,即返回得到T&&。根据引用折叠原则,T&&会保留T原来的左值、右值属性。std::declval()返回类型时,比直接使用T()好处是:"T()"要求T是内置类型,或是拥有默认构造函数的类;而"std::declval()"则没有这个要求。代价是:std::declval并不是一个普通函数,而是常与结合decltype用于类型推导,禁止调用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

posted @ 2022-05-02 17:56  明明1109  阅读(1263)  评论(0编辑  收藏  举报