C++模板 匿名类型参数

模板的类型参数, 除了能够定义默认类型之外.
还可以定义匿名类型参数, 这个匿名类型参数不能被函数体使用, 也不能被返回值使用.
匿名类型参数利用替换失败不是错误的特性来判定, 如果没有出现替换失败, 那么就是符合匹配的.

 

什么场景下需要用到匿名类型参数?

C++ 模板 SFINAE 这篇文章中第二份代码中的一个片段来举例.
这个函数声明了 class U 这个类型参数, 并且设定了一个默认值T::author_type,
但是函数体、返回值中都没有使用到 U 这个类型, 如果实际项目中函数体的代码非常复杂,
多出一个不明确的类型是非常恐怖的一件事情, 而匿名类型参数可以减少这个心里负担.

template<typename T, class U = typename T::author_type>
void example(T t) {
    cout << "template<typename T = book::author_type > void example();" << endl;
}

 

所以, 匿名类型参数就是用来降低心里复杂度的蜜枣.

由于 struct book 没有定义 author_type 这个成员类型, 因此 T::author_type 是一个无效的调用, 编译器不会报错, 而是选择跳过当前匹配进而去寻找其他匹配.

#include <iostream>
using std::cout;
using std::endl;


struct book {
    typedef int page_type;      
    using name_type = string;
};


// class = typename T::author_type 就是一个匿名类型参数.
template<typename T, class = typename T::author_type>
void example(T t) {
    cout << "template<typename T = book::author_type > void example();" << endl;
}

// template<typename T, class = typename T::page_type> 
// 等同于 
// template<typename T, typename = int>   
// 第二个类型参数是一个匿名类型参数(没有提供一个identifier), 并且提供了一个int作为默认类型.
template<typename T, class = typename T::page_type>
void example(T t) {
    cout << "void example(book b)" << endl;
}


int main(void) {
    book b;
    example(b);
    return 0;
}

 

展开讨论模板参数的定义方式

  1. 什么是 Identifier?

    template <typename T>    // T 就是 Identifier.
    void example() {};
    
  2. Identifier 的命名不能跟 C++ 中的关键字出现冲突.

    // 错误的
    // 因为 identifier 占用和覆盖了 int , 会导致后续功能不正常, 因此编译是不会通过的.
    template<typename int>
    void normal(int s){
        cout << "normal: " << s << endl;
    }
    
    // 正确的
    // 但是建议使用常见的 T 或者 U 来当作 Identifier.
    template<typename aaa>
    void normal(aaa s){
        cout << "normal: " << s << endl;
    }
    
  3. template <>typenameclass 有什么区别?
    没有区别, 但是建议统一风格, 只使用其中任意一种,
    我个人基本都是使用typename.

  4. typename 这个关键字是什么含义?
    它是一个标识符, 即便不放在 template <> 中, 也可以再常规的函数体中使用, 例如: typename int a = 10;
    但是在 template<> 中, 要声明一个类型, 必须要使用 typenameclass 标识符, 用来标识它是一个Identifier.

  5. 具体化和匿名类型参数的区别?
    具体化有一个 Identifier 而匿名类型参数没有 Identifier.

    #include <iostream>
    using std::cout;
    using std::endl;
    
    
    // 具体化
    // int 是一个具体的类型
    // n 是一个Identifier 也是一个具体值, 所以不能用 n 来声明一个变量.
    template<int n>                           // 也可以写成: template<typename int n>
    void specialization() {
        cout << n << endl;
    };
    
    
    // 具体化 和 匿名类型参数
    // 因为 int 是一个具体类型, 所以不能也没必要定义一个 Identifier , 所以函数体无法使用这个参数.
    // 匿名类型参数必须要有一个默认值.
    template<int = 0>                        // 也可以写成: template<typename int = 0>
    void specialization_and_anonymous() {
        cout << "specialization_and_anonymous" << endl;
    };
    
    
    int main(void) {
        specialization<10>();                       // 必须这样使用.
        specialization_and_anonymous();
        // output:
        // 10
        // specialization_and_anonymous
        return 0;
    }
    

参考

class = typename T::type
sfinae cppreference

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容