3.2 虽旧犹新的语言特性

非类型模板参数

1.除了类型参数之外,我们也可以为template使用nontype paramatter.

2.非类型参数看作是template类型的一部分

bitset<32> flags32;
bitset<50> flags50;
// 这两个看作是两个不同类型的template

模板参数默认值

class template 可以拥有默认参数

function template也可以拥有默认参数

// 类模板
template <typename T1, typename T2 = int>
class TmpClass1 {};

// no default argument for 'T2',感觉有点像函数的默认参数,一个参数有了默认值,那么这个参数后面的参数都必须有默认值
// 通过查阅资料:类模板的默认模板参数从右往左开始指定
// template <typename T1 = double, typename T2>
// class TmpClass2 {};

// 函数模板
// 这两个都能编译通过
template <typename T1 = double, typename T2>
void func(T1 arg1, T2 arg2) {}

template <typename T1, typename T2 = double>
void func1(T1 arg1, T2 arg2) {}

关键字typename

关键值typename用来指明后面紧跟的是个类型

注意事项:C++的一般性规则是,template内的任何标识符都被视为一个value,除非它加上typename

#include <iostream>

class Q
{
public:
    using SubType = int;
};

template <typename T>
class MyClass
{
    // 这样写编译器会直接提示需要在前面加上typename
    // T::SubType *ptr;
    typename T::SubType *ptr;

public:
    MyClass() {}
    ~MyClass() {}
};

int main(int argc, char const *argv[])
{
    /* code */
    MyClass<Q> myclassq;
    return 0;
}

成员模板

class的成员函数可以是一个template。然而member template不可以是virtual

通常用来支持class template内的成员之间的自动类型转化。

#include <iostream>
// 基本用法
class MyClass
{
    public:
        MyClass() {}
        ~MyClass() = default;
    template<typename T>
    void f(T a)
    {
        // 这里面的操作是T这个类型支持的。
        ;//
    }
};

// 常用用法
template<typename T>
class MyClass1 
{
    private:
        T value;
    public:
        template <typename X>
        void assign(const MyClass1<X>&x)
        {
            value = x.getValue();
        }

        T getValue() const 
        {
            return value;
        }
};

void f()
{
    MyClass1<double> d;
    MyClass1<int> i;
    d.assign(d);
    d.assign(i); 
}

// 特殊情况: member template 构造函数
template <typename T>
class MyClass2
{
public:
    // 没有这个会报错:error: no matching function for call to 'MyClass2<double>::MyClass2()'
    MyClass2() {}
    template <typename U>
    MyClass2(const MyClass2<U> &x)
    {
            std::cout << "MyClass(const MyClass<U>&x)" << std::endl;
    }
};

void f1()
{
    MyClass2<double> xd;
    MyClass2<double> xd2(xd);
    MyClass2<int> xi(xd);

    // 输出结果:只有一次MyClass(const MyClass<U>&x)
    // 通过查阅资料:发现当xd2与xd类型相同时,通过隐式生成的copy构造函数完成。类型不同的时候才通过我们写的那个template
}

int main(int argc, char const *argv[])
{
    f();
    f1();
    return 0;
}

嵌套式的class template

template <typename T>
class MyClass
{
	template<typename T2>
	class NestedClass {};
};

3.2.1 基础类型的明确初始化

知识点:

“一个明确的构造函数调用,但不给实参”这样的语法,基础类型会被设定初值0

如果一个template强迫设置初值为0,其值就是所谓的zero initialized,否则就是default initialized

int i1;         // undefined value 
int i2 = int(); // 0
int i3{};       // 0

利用这一个特性

template <typename T>
void f()
{
    T x = T();
    std::cout << x << std::endl;
}

3.2.2 main()定义式

// 这个是我这补全工具自动补全生成的,没有报错。
int main(int argc, char const *argv[])
{
}

// main的合法定义式
int main()
{

}

int main(int argc, char **argv)
{

}

int main(int argc, char *argv[])
{

}
// 通常情况下,会写一个return 0,但是不是必须。
// 可以使用exit()/quick_exit()/terminate()
09-16 11:04