理解C++标准库模板:enable_if

目录

1.    说明

2.    成员类型

2.1  类型定义

2.2  注意事项

3.    示例


1.    说明

         定义于头文件  <type_traits>

template< bool B, class T = void >
struct enable_if;

(since C++11)

如果 B 为真,则 std::enable_if 具有公共成员 typedef 类型,等于 T;否则,没有成员 typedef。此超越函数是利用 C++20 之前的概念的 SFINAE (Substitution Failure Is Not An Error)的一种便捷方式,特别是用于根据类型特征有条件地从候选集中删除函数,从而允许基于这些不同类型特征(trait)进行单独的函数重载或特化。

         std::enable_if 可用于多种形式,包括

•  作为附加函数参数(不适用于大多数运算符重载),

•  作为返回类型(不适用于构造函数和析构函数),

•  作为类模板或函数模板参数。

如果程序为 std::enable_if添加特化,则行为未定义。

2.    成员类型

2.1  类型定义

类型定义:   

类型为  T 或“无”这样的成员,取决于B 的值。

辅助类型:   

template< bool B, class T = void >
using enable_if_t = typename enable_if<B,T>::type;

(since C++14)

可能的实现:

template<bool B, class T = void>

struct enable_if {};

 

template<class T>

struct enable_if<true, T> { typedef T type; };

2.2  注意事项

         一个常见的错误是声明两个仅在默认模板参数方面不同的函数模板。这是行不通的,因为声明被视为对同一函数模板的重新声明(默认模板参数不考虑函数模板等价性)。

/* 以下错误 */

 

struct T

{

    enum { int_t, float_t } type;

 

    template<typename Integer,

             typename = std::enable_if_t<std::is_integral<Integer>::value>>

    T(Integer) : type(int_t) {}

 

    template<typename Floating,

             typename = std::enable_if_t<std::is_floating_point<Floating>::value>>

    T(Floating) : type(float_t) {} // 错误: 视为重定义

};

 

/* 以下正确 */

 

struct T

{

    enum { int_t, float_t } type;

 

    template<typename Integer,

             std::enable_if_t<std::is_integral<Integer>::value, bool> = true>

    T(Integer) : type(int_t) {}

 

    template<typename Floating,

             std::enable_if_t<std::is_floating_point<Floating>::value, bool> = true>

    T(Floating) : type(float_t) {} // OK

};

在命名空间范围函数模板的模板非类型参数类型中使用 enable_if 时应小心谨慎。某些 ABI 规范(如 Itanium ABI)(注:ABI—Application Binary Interface(应用程序二进制编程接口))未在混淆中包含非类型模板参数的实例化相关部分,这意味着两个不同函数模板的特化可能会以相同的混淆名称结束,并错误地链接在一起。例如:

// 1个编译单元

 

struct X

{

    enum { value1 = true, value2 = true };

};

 

template<class T, std::enable_if_t<T::value1, int> = 0>

void func() {} // #1

 

template void func<X>(); // #2

 

// 2个编译单元

 

struct X

{

    enum { value1 = true, value2 = true };

};

 

template<class T, std::enable_if_t<T::value2, int> = 0>

void func() {} // #3

 

template void func<X>(); // #4

函数模板 #1 和 #3 具有不同的签名,是不同的模板。尽管如此,#2 和 #4 尽管是不同函数模板的实例,但在 Itanium C++ ABI (_Z4funcI1XLi0EEvv) 中具有相同的混淆名称,这意味着链接器会错误地将它们视为同一实体。

3.    示例

#include <iostream>

#include <new>

#include <string>

#include <type_traits>

 

namespace detail

{

    void* voidify(const volatile void* ptr) noexcept { return const_cast<void*>(ptr); }

}

 

// #1, 通过返回类型使能

template<class T>

typename std::enable_if<std::is_trivially_default_constructible<T>::value>::type

    construct(T*)

{

    std::cout << "default constructing trivially default constructible T\n";

}

 

// 与上述相同

template<class T>

typename std::enable_if<!std::is_trivially_default_constructible<T>::value>::type

    construct(T* p)

{

    std::cout << "default constructing non-trivially default constructible T\n";

    ::new(detail::voidify(p)) T;

}

 

// #2

template<class T, class... Args>

std::enable_if_t<std::is_constructible<T, Args&&...>::value> // 利用帮助类型

    construct(T* p, Args&&... args)

{

    std::cout << "constructing T with operation\n";

    ::new(detail::voidify(p)) T(static_cast<Args&&>(args)...);

}

 

// #3,通过一个参数使能

template<class T>

void destroy(

    T*,

    typename std::enable_if<

        std::is_trivially_destructible<T>::value

    >::type* = 0)

{

    std::cout << "destroying trivially destructible T\n";

}

 

// #4, 通过非类型模板参数使能

template<class T,

         typename std::enable_if<

             !std::is_trivially_destructible<T>{} &&

             (std::is_class<T>{} || std::is_union<T>{}),

             bool>::type = true>

void destroy(T* t)

{

    std::cout << "destroying non-trivially destructible T\n";

    t->~T();

}

 

// #5, 通过模板参数使能

template<class T,

         typename = std::enable_if_t<std::is_array<T>::value>>

void destroy(T* t) // note: function signature is unmodified

{

    for (std::size_t i = 0; i < std::extent<T>::value; ++i)

        destroy((*t)[i]);

}

 

/*

template<class T,

         typename = std::enable_if_t<std::is_void<T>::value>>

void destroy(T* t) {} // error: has the same signature with #5

*/

 

// 通过一个模板参数使能A的部分特化

template<class T, class Enable = void>

class A {}; // 主模板

 

template<class T>

class A<T, typename std::enable_if<std::is_floating_point<T>::value>::type>

{}; // 对浮点特化

 

int main()

{

    union { int i; char s[sizeof(std::string)]; } u;

 

    construct(reinterpret_cast<int*>(&u));

    destroy(reinterpret_cast<int*>(&u));

 

    construct(reinterpret_cast<std::string*>(&u), "Hello");

    destroy(reinterpret_cast<std::string*>(&u));

 

    A<int>{}; // OK: 匹配主模板

    A<double>{}; // OK: 区配部分特化

}

输出:

default constructing trivially default constructible T

destroying trivially destructible T

constructing T with operation

destroying non-trivially destructible T

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值