类型基础

在回顾模板之前,需要明确一个概念:模板编程是针对类型的计算。。这和我们平时的代码不同,我们平时写的程序都是针对数据的。

在模板元编程中,typename 用于定义类型;using 用于给模板类型赋值,注意这里的赋值和变量的赋值意义不同。参考下面的代码:

#include <iostream>using LL = long long;struct Foo {using T1 = int;using T2 = float;
};int main() {LL n = 1e10;std::cout << n << std::endl;Foo::T1 a = 10;Foo::T2 b = 100.0;std::cout << a << ", " << b << std::endl;std::cout << sizeof(Foo) << std::endl;  // 类型不占用结构体的sizereturn 0;
}/*
10000000000
10, 100
1
*/

模板一般和 struct 共同使用,类型不占用结构体的size。类型的可见性和成员变量的可见性一致,比如:

#include <iostream>
class Foo {using T = int;
public:static void foo() {T a = 10;  // 这里可用,非 static 同样可用 Tstd::cout << a << std::endl;}
};int main() {// Foo::T a = 10;   // private 函数不可用Foo::foo();return 0;
}

根绝这个例子,我们可以把类型认为是不占用空间的成员。因此,如果是继承关系,那么子类会继承父类的类型,可见性和成员变量的一致。代码实例:

#include <iostream>struct Base {using T1 = int;
};struct Derived: Base {using T2 = float;
};int main() {Derived::T1 a = 10;Derived::T2 b = 1.1;std::cout << a << ", " << b << std::endl;return 0;
}

之后,我们再看typename 关键字,用于定义类型:

#include <iostream>template<typename T>
struct Foo {T a;
};int main() {Foo<int> foo{};foo.a = 10;std::cout << foo.a << std::endl;return 0;
}

对于这种情况,class 也可以胜任。但是typename有个更特殊的功能,用于声明类型:

#include <iostream>template<typename T>
struct Foo {void foo() {typename T::t a = 10;  // T::t显式说明这是类型(1)std::cout << a << std::endl;}
};struct Type {using t = int;
};int main() {Foo<Type> f{};f.foo();Foo<int> f1{};  // 这里声明不会报错(2)// f1.foo();  // 这里会报错(3)return 0;
}

上面的代码(1)中,typename 显式说明 T::t是类型。因为,T::t 也可以作为成员函数或者成员变量:

#include <iostream>template<typename T>
struct Foo {void foo() {T::t();  // 这里作为成员变量了}
};struct Type {static void t() {std::cout << "type test" << std::endl;}
};int main() {Foo<Type> foo{};foo.foo();return 0;
}

SFINAE

Substitution Faile Is Not An Error

C++在执行模板匹配的时候,如果有多个候选项,那么当一个匹配不上的时候,不会报错,而是会继续匹配其他的,直到有最佳的匹配项,或者都匹配不上报错为止。

当然,也有优先匹配的原则,这个就是模板偏化和特化,这里不再特殊介绍。

直接给出一个 SFINAE 的例子:

#include <iostream>
#include <type_traits>template<typename T>
class Future {};template<typename T>
struct IsFuture: std::false_type {};template<typename T>
struct IsFuture<Future<T>>: std::true_type {};int main() {// 这里匹配到 Future<T>,所以类型是 Truestd::cout << IsFuture<Future<int>>::value << std::endl;// 匹配不到Future,会去默认的std::cout << IsFuture<int>::value << std::endl;return 0;
}

std::enable_if

SFNIAE 最经典的例子,就是std::enable_if 了,具体用法看手册,给出一个简单的例子:

#include <iostream>
#include <type_traits>template<typename T>
std::enable_if_t<std::is_same_v<T, int>,double> foo(T n) {return n * 2.0;
}template<typename T>
std::enable_if_t<std::is_same_v<T, double>,int> foo(T n) {return int(n) % 2;
}int main() {std::cout << foo(10) << std::endl;  // --> 20std::cout << foo(1.5) << std::endl;  // --> 1// std::cout << foo("aaa") << std::endl;  // (1)return 0;
}

(1)没有对应的定义,无法生成定义。

上面是个简单的例子,当然了,这个通过函数重载也可以做。我们给出一个无法通过函数重载做的例子:

#include <iostream>
#include <type_traits>template<typename F, typename ...Args>
std::enable_if_t<std::is_same_v<std::invoke_result_t<F, Args...>, int>,double> foo(F &&f, Args &&... args) {int n = std::forward<F>(f)(std::forward<Args>(args)...);return n * 2.0;
}template<typename F, typename ...Args>
std::enable_if_t<std::is_same_v<std::invoke_result_t<F, Args...>, double>,int> foo(F &&f, Args &&... args) {double n = std::forward<F>(f)(std::forward<Args>(args)...);return int(n) % 2;
}int f(int, double) {return 10;
}double f1(double, int) {return 3.0;
}void f2() {}int main() {int a = 10;std::cout << foo(f, a, 1.0) << std::endl;std::cout << foo(f1, 10.0, a) << std::endl;// std::cout << foo(f2) << std::endl;  // 错误,没有这种类型重载return 0;
}

意义是,foo 函数的参数是一个函数ff对应的值,之后根据f的返回值来特化出不同的版本。这种例子,是函数重载根本无法实现的。

参考文档

http://kaiyuan.me/2018/05/08/sfinae/
https://marvinsblog.net/post/2019-09-11-cpp-sfinae-intro/
https://zhuanlan.zhihu.com/p/21314708

SNIFE 和 std::enable_if相关推荐

  1. std::enable_if的用法

    一.测试程序1 #include <iostream> #include <type_traits> using namespace std; template <typ ...

  2. C++ std::enable_if的简明指南

    1. 简介 std::enable_if是在C++ 11中引入的模板结构体,在头文件<type_traits>中定义. template < bool B, class T = vo ...

  3. C++11模板元编程—std::enable_if使用说明

    std::enable_if 顾名思义,满足条件时类型有效.作为选择类型的小工具,其广泛的应用在 C++ 的模板元编程中.它的定义也非常的简单: // STRUCT TEMPLATE enable_i ...

  4. std::enable_if

    std::enable_if 顾名思义,满足条件时类型有效.作为选择类型的小工具,其广泛的应用在 C++ 的模板元编程(meta programming)中.它的定义也异常的简单: template ...

  5. std::enable_if 的几种用法

    https://yixinglu.gitlab.io/enable_if.html std::enable_if 的几种用法 tech 12cpp 2 std::enable_if 顾名思义,满足条件 ...

  6. (C++模板编程):std::enable_if的使用(下)

    目录 std::enable_if的使用 std::enable_if std::enable_if源码 偏特化完全可以理解成一种(在编译期)条件分支语句. std::enable_if基础认识 en ...

  7. c++11 std::enable_if在模板偏特化的妙用

    1.模板自动推导功能. 先看个例子: 在调用TestTemplate函数时, 我们可以在函数后面加上<类型>无歧义地指定调用的版本. 结果如下: 由于模板参数在函数参数中的位置是固定的,编 ...

  8. std::bind技术内幕

    引子 最近群里比较热闹,大家都在山寨c++11的std::bind,三位童孩分别实现了自己的bind,代码分别在这里: 木头云的实现 mr.li的实现 null的实现,null的另一个版本的实现 这些 ...

  9. C++11 enable_if 详解

    enable_if<>这个概念,以前从没有遇到过,这里做个小记. /*----------llvm/include/llvm/ADT/Hashing.h------------*/ /// ...

最新文章

  1. AMD64,linux-64bit,ARM64,linux-Aarch64和windows 64bit
  2. 带有.rdlc报表的项目发布需要注意的问题
  3. caffe学习(六):使用python调用训练好的模型来分类(Ubuntu)
  4. 炼丹面试官的面试笔记
  5. Node.js基本讲解
  6. Sql 行转列问题总结
  7. pytorch学习笔记(6):GPU和如何保存加载模型
  8. EqualLogic强势增长,戴尔领跑iSCSI中国及香港地区市场
  9. js系列教程4-函数、函数参数全解
  10. 数学运算模块:Python3.7的math模块与cmath模块
  11. 数学公式编辑器AxMath
  12. 有道词典生词本到excel的装换
  13. python whl文件安装_python whl文件怎么安装
  14. Beetlsql自学笔记
  15. 第六章 平均绝对误差(MAE)与均方根误差(RMSE)
  16. 基于知识图谱的智能问答机器人技术架构
  17. 叫你怎么设置手机成空号
  18. ui设计入门书籍_书籍封面设计入门指南
  19. zkSnarks:QAP上构造零知识证明
  20. 烧录flash_烧录固件完成后,配置JFLASH让程序自动运行

热门文章

  1. python3.7模块内容_python3.7 time模块
  2. 嵌入不同源的页面_嵌入式技术课程教与学(教学大纲和试卷)
  3. 数据结构 2-0 线性表总结
  4. 目标检测——模型效率的优化
  5. 为什么单片机的代码在Flash中运行,单片机的代码运行位置跟电脑有什么不同?
  6. ROS学习笔记十一:ROS中数据的记录与重放
  7. MySQL学习记录 (五) ----- 存储函数、存储过程和触发器
  8. .Net 获取IP 地址和计算机名(本地网)
  9. CRM【第三篇】: crm业务
  10. 第6集_奇点和安迪吃饭1 第一次见面