0
点赞
收藏
分享

微信扫一扫

(P15-P16)对模板右尖括号的优化、函数模板的默认模板参数


文章目录

  • 1. 模板的右尖括号
  • 2.默认模板参数

1. 模板的右尖括号

在泛型编程中,模板实例化有一个非常繁琐的地方,那就是连续的两个右尖括号(>>)会被编译器解析成右移操作符,而不是模板参数表的结束。

  • 我们先来看一段关于容器遍历的代码,在创建的类模板 Base 中提供了遍历容器的操作函数 traversal():
  • eg:对容器进行遍历,容器有两种:关系型的和非关系型的,关系型容器是键值对

// test.cpp
#include <iostream>
#include <vector>
using namespace std;

template <typename T>
class Base
{
public:
    void traversal(T& t)
    {
        auto it = t.begin();
        for (; it != t.end(); ++it)
        {
            cout << *it << " ";
        }
        cout << endl;
    }
};


int main()
{
    vector<int> v{ 1,2,3,4,5,6,7,8,9 };
    Base<vector<int>> b;
    b.traversal(v);

    return 0;
}

  • 测试:
  • (P15-P16)对模板右尖括号的优化、函数模板的默认模板参数_#include

  • 按照03标准进行编译,方法:

g++ test.cpp -std=c++03 -o app
就会得到如下的错误提示:

test.cpp:25:20: error: '>>' should be '> >' within a nested template argument list
     Base<vector<int>> b;

根据错误提示中描述模板的两个右尖括之间需要添加空格,这样写起来就非常的麻烦,C++11改进了编译器的解析规则,尽可能地将多个右尖括号(>)解析成模板参数结束符,方便我们编写模板相关的代码。

  • 上面的这段代码,在支持 C++11 的编译器中编译是没有任何问题的,如果使用 g++ 直接编译需要加参数 -std=c++11:

g++ test.cpp -std=c++11 -o app

2.默认模板参数

在 C++98/03 标准中,类模板可以有默认的模板参数:

#include <iostream>
using namespace std;

template <typename T=int, T t=520>
class Test
{
public:
    void print()
    {
        cout << "current value: " << t << endl;
    }
};

int main()
{
    Test<> t;
    t.print();

    Test<int, 1024> t1;
    t1.print();

    return 0;
}

  • 测试:

    但是03不支持函数的默认模板参数在C++11中添加了对函数模板默认参数的支持:

#include <iostream>
using namespace std;

template <typename T=int>	// C++98/03不支持这种写法, C++11中支持这种写法
void func(T t)
{
    cout << "current value: " << t << endl;
}

int main()
{
    func(100);
    return 0;
}

  • 测试:
  • (P15-P16)对模板右尖括号的优化、函数模板的默认模板参数_开发语言_02

  • 结论:

通过上面的例子可以得到如下结论:当所有模板参数都有默认参数时,函数模板的调用如同一个普通函数。但对于类模板而言,哪怕所有参数都有默认参数,在使用时也必须在模板名后跟随 <> 来实例化。

另外:函数模板的默认模板参数在使用规则上和其他的默认参数也有一些不同,它没有必须写在参数表最后的限制。 这样当默认模板参数和模板参数自动推导结合起来时,书写就显得非常灵活了。

  • 我们可以指定函数模板中的一部分模板参数使用默认参数,另一部分使用自动推导,比如下面的例子:

#include <iostream>
#include <string>
using namespace std;

template <typename R = int, typename N>
R func(N arg)
{
    return arg;
}

int main()
{
	//函数返回值类型使用了默认的模板参数,函数的参数类型是自动推导出来的为 int 类型。
    auto ret1 = func(520);
    cout << "return value-1: " << ret1 << endl;
	
	//函数的返回值指定为 double 类型,函数参数是通过实参推导出来的,为 double 类型
    auto ret2 = func<double>(52.134);
    cout << "return value-2: " << ret2 << endl;
	
	//函数的返回值指定为 int 类型,函数参数是通过实参推导出来的,为 double 类型
    auto ret3 = func<int>(52.134);
    cout << "return value-3: " << ret3 << endl;
	
	//函数的参数为指定为 int 类型,函数返回值指定为 char 类型,不需要推导
    auto ret4 = func<char, int>(100);
    cout << "return value-4: " << ret4 << endl;

    return 0;
}

  • 测试:
  • 默认模板参数模板参数自动推导同时使用时(优先级从高到低):

如果可以推导出参数类型则使用推导出的类型
如果函数模板无法推导出参数类型,那么编译器会使用默认模板参数
如果无法推导出模板参数类型并且没有设置默认模板参数,编译器就会报错。

  • eg:

#include <iostream>
#include <string>
using namespace std;

// 函数模板定义
template <typename T, typename U = char>
void func(T arg1 = 100, U arg2 = 100)
{
    cout << "arg1: " << arg1 << ", arg2: " << arg2 << endl;
}

int main()
{
    // 模板函数调用
    //func('a'):参数 T 被自动推导为 char 类型,U 使用的默认模板参数为 char 类型
    func('a');
    //func(97, 'a');:参数 T 被自动推导为 int 类型,U 使用推导出的类型为 char
    func(97, 'a');
    //func();:参数 T 没有指定默认模板类型,并且无法自动推导,编译器会直接报错
    /*
	模板参数类型的自动推导是根据模板函数调用时指定的实参进行推断的,没有实参则无法推导
	模板参数类型的自动推导不会参考函数模板中指定的默认参数。
	*/
    // func();    //编译报错
    return 0;
}

  • 测试:
  • 参考:模板的优化


举报

相关推荐

0 条评论