理解UE4中C++17的...符号及enable_if_t的用法及SFINAE思想

下面是一段C++17的代码:

//函数1:

template <typename... BufferTypes,

std::enable_if_t<std::conjunction<CanAppendBufferType<std::decay_t<BufferTypes>>...>::value> * = nullptr>

inline explicit FCompositeBuffer(BufferTypes&&... Buffers)

{

if constexpr (sizeof...(Buffers) > 0)

{

Segments.Reserve((GetBufferCount(Forward<BufferTypes>(Buffers)) + ...));

(AppendBuffers(Forward<BufferTypes>(Buffers)), ...);

Segments.RemoveAll(&FSharedBuffer::IsNull);

}

}

红色字体表示语法上的整体。

1、 template <typename... BufferTypes, 表示声明若干个类型名;

2、CanAppendBufferType< std::decay_t<BufferTypes> > 这是一个关于类型的调用,具体含义先忽略;

3、std::conjunction<CanAppendBufferType<std::decay_t<BufferTypes>>...>

将它看成此形式: std::conjunction< condition<BufferTypes>... >

此时,红色字体是一个整体,... 将会unpack(解压) condition<SomeArgs> 这个模式,并以逗号分割的参数形式,传递给 std::conjunction ,即等价于:

std::conjunction< condition<BT1>, condition<BT2>, condition<BT3> >

4、std::conjunction< a, b, c >::value 的意思是 a || b || c 的值,但是,它是模板元编程,也就是在"编译时期"计算 a||b||c 的,不会影响运行时性能,且不能用 || 来代替,且a\b\c 都是编译时确定值的变量,而非类型。

5、std::enable_if_t< some_value > 其实是 std::enable_if_t< some_value , void > 的简称,且 std::enable_if_t 的定义是:

复制代码
template <bool _Test, class _Ty = void>
using enable_if_t = typename enable_if<_Test, _Ty>::type;

enable_if<the_value, the_type> ::type 的 意思是

如果the_value是true,则定义为形式2,如果the_value是false,则定义为形式1 。

复制代码
template <bool _Test, class _Ty = void>
struct enable_if {}; // no member "type" when !_Test   //形式1

template <class _Ty>
struct enable_if<true, _Ty> { // type is _Ty for _Test  //形式2
    using type = _Ty;
};

此时根据 C++17模板元编程的原则 "SFINAE(Substitution Failure Is Not An Error,替换失败并非错误)" ,如果找不到 ::type ,那么就忽略掉模板函数,而非报错。

同时,enable_if_t 是 typename enable_if<_Test, _Ty>::type; 的昵称。因此 std::enable_if_t< some_value > 的意思是,如果有 some_value 的值,那么此处就以 void 来定义,如果没有,那么就当该函数 (函数1)不存在。

6、template <typename... BufferTypes, some_type * = nullptr>

由于5的论述,我们知道,some_type 只可能是 void ,否则把该模板函数(函数1)当做不存在。那么上述表达等价于:

template <typename... BufferTypes, void * = nullptr>

不妨补充其匿名的名称为 secretboy ,如下

template <typename... BufferTypes, ( void * ) secretboy = nullptr>

也就是说,逗号的后面,是一个值(而不是类型),且该值没有名字,它不会被函数体用到,那么它无关紧要。既然无关紧要,为啥还需要它呢?因为要让它的计算起到 SFINAE 的作用。所以,如果5合法,那么 它等价于

template <typename... BufferTypes >

如果5不合法,那么忽略函数1的存在。

7、 if constexpr (sizeof...(Buffers) > 0)

表示如果 Buffers 有真实的数量,那么 就需要编译下面的代码,否则不用编译。

8、(GetBufferCount(Forward<BufferTypes>(Buffers)) + ...)

我们把它简化为这个形式: (func(BufferTypes) + ... )

其中的 + ,在语法上还可以是 - (减号) || (或者),甚至是 , (逗号) 。

也就是说,函数调用后的 参数包 ,可以展开,等价于:

func(BT1) + func(BT2) + ... (式子8)

并且请注意, args + ... 的展开等价于 a + ( b + ( c + d ) ) ,所以严格来说,式子8不够严谨,应该等价于:

func(BT1) + ( func(BT2) + ( ... ) ) (式子9)

相关推荐
Mr_WangAndy24 分钟前
C++设计模式_创建型模式_原型模式Prototype
c++·设计模式·原型模式
奔跑吧邓邓子28 分钟前
【C++实战㊷】C++ 原型模式实战:从概念到高效应用
c++·实战·原型模式
LeaderSheepH43 分钟前
常见的排序算法
数据结构·算法·排序算法
奔跑吧邓邓子1 小时前
【C++实战㊶】C++建造者模式:复杂对象构建的秘密武器
c++·实战·建造者模式
奔跑吧邓邓子1 小时前
【C++实战㊵】C++抽象工厂模式:解锁高效对象创建的密钥
c++·实战·抽象工厂模式
jf加菲猫2 小时前
条款11:优先选用删除函数,而非private未定义函数
开发语言·c++
JasmineX-13 小时前
数据结构——静态链表(c语言笔记)
c语言·数据结构·链表
怀旧,3 小时前
【C++】23. C++11(上)
开发语言·c++
小卡皮巴拉4 小时前
【笔试强训】Day1
开发语言·数据结构·c++·算法
初圣魔门首席弟子4 小时前
switch缺少break出现bug
c++·算法·bug