在模板开发中有时需要判断一个类中是否含特定的方法,然后根据判断结果开分支,编写不同的逻辑。下面就介绍一下最常用的一种方式:
template <typename U>
struct class_str {
template <typename T, string(T::*)() = &T::str>
static constexpr bool check(T*) { return true; }; // (1)
static constexpr bool check(...) { return false; }; // (2)
static constexpr bool ret = check(static_cast<U*>(0)); // (3)
};
上面的代码将判断U中是否含有string str();方法,如果含的,则ret = true,否则ret = false。
在(3)中给check()方法传入了类型为U*的空指针,运行过程如下:
- 如果U中含有string str();方法,则(1)和(2)两个check()方法将都会在编译期生成,但check(T*)最匹配static_cast<U*>(0)的结果,所以这时会调用(1)这个check(T*),返回true;
- 如果U中不含有string str();方法,则只有(2)这个check()方法会在编译期生成, 于是调用(2)后返回false。
为了更好的看到效果,我们把“C++:举例说明如何使用enable_if和模板的函数指针参数“客户的例子改进一下:
#include <string>
#include <iostream>
using namespace std;
class Box {
public:
string str() {
return "yes";
}
};
class Bin {
public:
string str1() {
return "no";
}
};
template <typename U>
struct class_str {
template <typename T, string(T::*)() = &T::str>
static constexpr bool check(T*) { return true; };
static constexpr bool check(...) { return false; };
static constexpr bool ret = check(static_cast<U*>(0));
};
// 不含有string str()方法的非std::string类
template<typename T,
typename std::enable_if<std::is_class<T>::value && !std::is_same<T, string>::value, T>::type* = nullptr,
typename std::enable_if<!class_str<T>::ret, T>::type* = nullptr>
std::string str(T& t) {
cout << "1.---------------------" << endl;
return "null";
};
// std::string类
template<typename T,
typename std::enable_if<std::is_class<T>::value && std::is_same<T, string>::value, T>::type* = nullptr>
std::string str(T& t) {
cout << "2.---------------------" << endl;
return t;
};
// 含有string str()方法的非std::string类
template<typename T,
typename std::enable_if<std::is_class<T>::value && !std::is_same<T, string>::value, T>::type* = nullptr,
typename std::enable_if<class_str<T>::ret, T>::type* = nullptr>
std::string str(T& t) {
cout << "3.---------------------" << endl;
return t.str();
};
// 数值型
template<typename T,
typename std::enable_if<!std::is_class<T>::value && std::is_arithmetic<T>::value, T>::type* = nullptr>
std::string str(T& t) {
cout << "4.---------------------" << endl;
return std::to_string(t);
};
int main() {
string s = "sddds";
cout << str<string>(s) << endl;
bool j = true;
cout << str<bool>(j) << endl;
int i = 1000;
cout << str<int>(i) << endl;
float f = 10.6f;
cout << str<float>(f) << endl;
Box b1;
cout << str<Box>(b1) << endl;
Bin b2;
cout << str<Bin>(b2) << endl;
return 1;
}
运行结果:
2.---------------------
sddds
4.---------------------
1
4.---------------------
1000
4.---------------------
10.600000
3.---------------------
yes
1.---------------------
null
以上代码中struct class_str使用了constexpr,这是C++11中才提供的,如果C++编译器不支持constexpr,则可以使用如下代码替代:
template <typename U>
struct class_str {
template <typename T, string(T::*)() = &T::str>
static char check(T*);
template <typename T>
static int check(...);
const static bool ret = sizeof(check<U>(static_cast<U*>(0))) == sizeof(char);
};
参考文档
Problem while checking if function exist in c++
Is there any way to detect whether a function exists and can be used at compile time?