boost::enable_if与SFINAE原则


模板函数的重载遵循SFINAE原则(substitution-failure-is-not-an-error):当一个模板函数的返回值或参数类型无效的时候,该实例不会参与重载解析,也不会导致编译错误。

利用这个原则,可以设计出具有选择性的模板函数。


先看一个例子:

int negate(int i) { return -i; }                                //1

template <class F>
typename F::result_type negate(const F& f) { return -f(); }     //2

当编译器遇到negate(1)的调用的时候,非模板函数1完全匹配,但是模板函数2也会尝试去实例化。显然无法用int去实例化模板函数2,这里如果没有SFINAE原则就会导致编译错误。实际上用int实例化的模板函数2不会出现在重载决议列表中,可能这个实例直接被删掉了。

boost::enable_if就是用来将某些不合要求的模板函数从重载决议列表中踢掉。

template <bool B, class T = void>
struct enable_if_c {
  typedef T type;
};

template <class T>
struct enable_if_c<false, T> {};

template <class Cond, class T = void>
struct enable_if : public enable_if_c<Cond::value, T> {};


可以看到enable_if实际上继承自enable_if_c,而模板enable_if_c需要两个模板参数
第一个是bool类型,来自enable_if的第一个参数的bool成员value,第二个参数是用户提供的类型T。

enable_if_c的作用就是如果第一个模板参数为true,那么它就将第二个模板参数typedef为type,否则什么也不干。那么enable_if_c<true, T>::type就是T,而enable_if_c<false, T>::type就是错误


template <class T>
typename enable_if_c<boost::is_arithmetic<T>::value, T>::type 
foo(T t) { return t; }

上面这个函数foo只接受算术类型参数

使用enable_if可以写得更简单一些:

template <class T>
typename enable_if<boost::is_arithmetic<T>, T>::type 
foo(T t) { return t; }

注意:
enable_if第一个模板参数必须是定义了bool成员value的一个类类型Cond
第二个参数为任意类型T
enable_if<true, T>::type为T
enable_if<false, T>::type无效

注意SFINAE只适用于模板函数,所以不能用于模板类的普通成员函数,
可以用于模板函数和模板类的模板成员函数。



从上面可以看出:不管enable_if_c的第一个参数是true还是false,第二个参数类型必须正确。但是有一种需求是这样的:第一个参数为true的时候,用户保证第二个参数有效,而第一个参数为false的时候用户不保证有效。这个时候就无法使用enable_if了,因为这时解析enable_if模板就已经失败了,这已经不是SFINAE的范畴的

例如:

template <class T, class U> class mult_traits;

template <class T, class U>
typename enable_if<is_multipliable<T, U>, 
                   typename mult_traits<T, U>::type>::type
operator*(const T& t, const U& u) { ... }

对于某些类型T和U的mult_traits特化版本,用户定义了类型成员type,其它情况没有
那么对于“其它情况”,mul_traits<T, U>::type就无效,导致解析enable_if模板失败,所以enable_if就实现不了这个功能了

lazy_enable_if可以用来解决这个问题

lazy_enable_if也接受两个模板参数:
第一个与enable_if一样,是一个定义bool成员value的一个类类型Cond。
第二个参数是一个有效类型T,并且当Cond::value是true的时候,
由用户保证typedef过T::type,此时lazy_enable_if这个模板类定义类型成员
    typedef T::type type,

当Cond::value是false时不要求用户定义T::type,同时lazy_enable_if也啥都不定义,
这样一来就避免了在解析lazy_enable_if这个模板的时候遇到错误,
而是在求lazy_enable_if<false, T>::type的时候出现错误

正确代码如下:
template <class T, class U> class mult_traits;

template <class T, class U>
typename lazy_enable_if<is_multipliable<T, U>, 
                        typename mult_traits<T, U> >::type
operator*(const T& t, const U& u) { ... }


注意:
lazy_enable_if接受第一个模板参数是一个定义bool成员value的一个类类型Cond,
当Cond::value为true时第二个参数必须是定义类型成员type的类类型
当Cond::value为false时第二个参数为任意类型。

lazy_enable_if<true, T>::type为T::type
lazy_enable_if<false, T>::type无效

<think>嗯,用户想了解C++中的enable_if的用法和示例。首先,我需要回忆一下enable_if的基本概念。记得enable_if是用于模板元编程中的SFINAE技术,也就是替换失败并非错误。当模板参数推导或替换失败时,编译器不会报错,而是继续寻找其他可行的模板。 根据用户提供的引用内容,enable_ifC++11中成为标准库的一部分,之前是在Boost里。引用[1]和[2]都提到了它的定义和使用方式。基本结构是有一个模板结构体enable_if,当第一个布尔参数为true时,才会有type成员,否则没有。C++14引入了enable_if_t来简化写法,避免写冗长的typename和::type。 用户可能需要知道如何在实际代码中使用enable_if。常见的应用场景包括函数模板的重载、类模板的特化,以及作为模板参数的默认值。比如,在函数返回值类型中使用enable_if,或者在模板参数中添加一个默认的enable_if条件。 举个例子,假设有一个函数模板,希望只在类型是整数时启用。这时候可以在返回类型中使用enable_if_t,条件用std::is_integral<T>::value。如果条件满足,返回类型就是void,否则该函数模板会被忽略,从而选择其他可能的重载。 另外,用户可能想知道如何避免常见的错误,比如确保条件表达式在编译时求值为布尔类型,以及如何处理多个重载时的优先级问题。这时候可能需要使用不同的条件或者结合其他类型特征,比如std::conditional或者concepts(C++20)。 在引用[3]中提到,示例代码需要C++20兼容的编译器,可能需要使用-std=c++20等编译选项。虽然用户的问题是关于enable_if的,但如果有机会的话,可以提到C++20中的concepts如何简化类似的需求,因为concepts提供了更清晰的语法。 用户可能的应用场景包括类型限制、条件编译不同的实现,或者在模板元编程中根据类型属性选择不同的路径。比如,序列化库中根据类型是否有特定的成员函数来选择不同的序列化方法。 还需要注意,当使用enable_if在函数参数中时,可能需要添加额外的默认参数,比如设置为void* = nullptr,或者使用额外的模板参数来应用enable_if。这可能涉及到函数签名的变化,需要确保不影响重载解析。 可能的相关问题包括SFINAE的其他应用、enable_ifconcepts的比较、常见的编译错误及其解决方法。比如,如果enable_if的条件写错了,导致始终无法匹配,编译器可能不会给出有意义的错误信息,这时候需要如何调试。 总结起来,回答的结构应该包括enable_if的基本定义、用法示例(函数返回值、模板参数、函数参数)、注意事项,以及C++20的替代方案。同时,引用提供的资料来支持解释,比如引用[1]和[2]中的结构定义和enable_if_t的说明。</think>### C++ enable_if 使用详解 #### 一、enable_if 核心原理 `std::enable_if` 是 SFINAE(Substitution Failure Is Not An Error)技术的核心工具,用于在模板实例化时进行条件编译控制[^2]。其基本结构定义如下: ```cpp template <bool B, typename T = void> struct enable_if {}; template <typename T> struct enable_if<true, T> { typedef T type; }; // 特化版本 ``` 当第一个模板参数为 true 时才会暴露 `type` 成员类型,否则该模板将被视为无效候选。 #### 二、核心使用场景 1. **函数模板条件控制** ```cpp // 仅对整型类型生效 template <typename T> typename std::enable_if_t<std::is_integral<T>::value, void> process_number(T num) { std::cout << "处理整型: " << num << std::endl; } // 仅对浮点类型生效 template <typename T> typename std::enable_if_t<std::is_floating_point<T>::value, void> process_number(T num) { std::cout << "处理浮点: " << num << std::endl; } ``` 2. **模板参数条件控制** ```cpp template <typename T, typename = std::enable_if_t<std::is_class_v<T>>> class Wrapper { // 仅当T是类类型时可用 }; ``` 3. **返回值类型推断(C++14起推荐写法)** ```cpp template <typename T> auto get_value(T t) -> std::enable_if_t<std::is_pointer_v<T>, typename std::remove_pointer_t<T>> { return *t; } ``` #### 三、现代C++改进方案 1. **使用 `enable_if_t` 简化(C++14+)** ```cpp template <typename T> std::enable_if_t<(sizeof(T) > 4), void> // 直接使用类型别名 handle_large_type() { ... } ``` 2. **结合 `void_t` 检测成员存在性(C++17+)** ```cpp template <typename, typename = void> struct has_serialize : std::false_type {}; template <typename T> struct has_serialize<T, std::void_t<decltype(std::declval<T>().serialize())>> : std::true_type {}; ``` 3. **使用概念(Concepts)替代(C++20+)** ```cpp template <typename T> requires std::integral<T> // 更直观的类型约束 void process(T val) { ... } ``` #### 四、典型错误及解决方案 1. **条件表达式错误** ```cpp // 错误:条件必须返回布尔值 std::enable_if_t<5, int> value; // 正确:使用类型特征转换 std::enable_if_t<std::is_integral_v<T>, int> ``` 2. **多条件冲突** ```cpp template <typename T> std::enable_if_t<std::is_integral_v<T>, void> func(T) {} // 版本1 template <typename T> std::enable_if_t<std::is_floating_point_v<T>, void> func(T) {} // 版本2 // 当T同时满足两个条件时会导致编译错误 ``` #### 五、应用场景示例 1. **迭代器类型分发** ```cpp template <typename Iter> void advance(Iter& it, int n) { advance_impl(it, n, typename std::iterator_traits<Iter>::iterator_category{}); } // 随机访问迭代器版本 template <typename Iter> void advance_impl(Iter& it, int n, std::random_access_iterator_tag) { it += n; } // 双向迭代器版本 template <typename Iter> void advance_impl(Iter& it, int n, std::bidirectional_iterator_tag) { if (n > 0) while (n--) ++it; else while (n++) --it; } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值