多平台编译问题——模板特例化

1.windows能编译过、linux不能编译通过的问题

1.1 模板特例化

1.1.1 问题描述:

如下代码

using namespace std;
class temp1{
public:
	int32_t nums;
};

class A {
public:
	template<typename T1>
	int32_t getNums(T1 t1) { return t1.nums; }

	template<>
	int32_t getNums(std::string str) {
		int32_t res = std::stoi(str);
		return res;
	}

};

在windows上能通过编译,在linux上没有通过编译,编译报错在template<>的“>”这个位置

1.1.2 解决办法

1.1.2.1 第一种,该特例化为重载

修改代码为:

using namespace std;
class temp1{
public:
	int32_t nums;
};

class A {
public:
	template<typename T1>
	int32_t getNums(T1 t1) { return t1.nums; }

	int32_t getNums(std::string str) {
		int32_t res = std::stoi(str);
		return res;
	}

};

取消特例化模板,改为重载

1.1.2.2 第二种,增加模板参数判断

1.1.2.1中所述方法,能编译成功,但是在执行时,可能会存在,比如传入std::string类型的数据,我们期望他走重载的方法,但是实际上他还是走的模板方法。

这时因为C++ 中的模板方法会优先于重载方法,尤其是在模板参数可以匹配的情况下。这种情况可以增加模板参数判断。

代码如下:

using namespace std;
class temp1{
public:
	int32_t nums;
};

class A {
public:
	template<typename T1, typename = typename std::enable_if_t<
    !std::is_same_v<T1,std::string>>
	int32_t getNums(T1 t1) { return t1.nums; }

	int32_t getNums(std::string str) {
		int32_t res = std::stoi(str);
		return res;
	}

};

有可能有些不支持c++17新特性,可以改用c++14特性

using namespace std;
class temp1{
public:
	int32_t nums;
};

class A {
public:
	template<typename T1, typename = typename std::enable_if<
    !std::is_same<T1,std::string>::value>::type>
	int32_t getNums(T1 t1) { return t1.nums; }

	int32_t getNums(std::string str) {
		int32_t res = std::stoi(str);
		return res;
	}

};

1.1.3 报错原因分析

windows上支持类中的模板函数特例化,但是linux上不支持类中的模板函数特例化。

1.1.4 延展

如下代码:

template<typename T1>
int32_t getNums(T1 t1) { return t1.nums; }

template<>
int32_t getNums(std::string str) {
    int32_t res = std::stoi(str);
    return res;
}

这样的模板函数,windows和linux环境都能支持。

总结:

  • 类模板函数,linux不支持特例化
  • 普通模板函数,windows和linux都能支持特例化

linux环境能不能特例化模板类,没有验证过。

1.1.5 其他可能出现的问题

如果模板方法,有两个入参,第一个参数是模板参数,第二参数带有默认参数,如下:

using namespace std;
class temp1{
public:
	int32_t nums=1024;
};


class temp2{
public:
	int32_t nums=0;
};

class A {
public:
    // 类模板方法
	template<typename T1>
	int32_t getNums(T1 t1,int32_t index = 0) { 
        return t1.nums; 
    }

    // 重载方法
	int32_t getNums(temp2 t2,int32_t index) {
		return t2.nums;
	}

};

如果在测试时

A a;
temp2 t2;
a.getNums(t2);

这时走的是类模板方法,而不是重载方法,因为没有传index的参数,只有类模板方法有默认的index入参。

如果想要调用重载方法,

那么需要传入index参数,这个没验证

a.getNums(t2,0);

或者给重载方法一个默认入参,重载方法修改为(这个验证过):

    // 重载方法
	int32_t getNums(temp2 t2,int32_t index = 0) {
		return t2.nums;
	}

这时编码时的疏忽造成的,因此为了保险起见,推荐1.1.2.2提供的方式,增加模板参数判断,这样不匹配时,就没有合适的方法被执行,方便排查问题。