在模板开发中有时需要判断一个类中是否含特定的方法,然后根据判断结果开分支,编写不同的逻辑。下面就介绍一下最常用的一种方式:

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);

C++11之美

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?

       在模板开发中有时需要判断一个类中是否含特定的方法,然后根据判断结果开分支,编写不同的逻辑。下面就介绍一下最常用的一种方式:template &amp;lt;typename U&amp;gt;struct class_str { template &amp;lt;typename T, string(T::*)() = &amp;amp;T::str&amp;gt; static cons...
C++模板中,SFINEA规则是指”Substitution failure is not an error“(匹配失败不是错误)。具体来说,就是当重载的模板参数展开时,如果展开导致一些型不匹配,编译器并不报错。 我们可以使用这个规则来判断是否存在某个成员函数,请看下面的实例: #include<iostream> #include<utility> #include<type_traits> template<typename T> struct
最近工作中遇到这样一个需求:实现一个ToString函数将型T转换到字符串,如果型T中含有同名方法ToString则直接调用。 这样一个ToString实现可以使用std::enable_if来做到,但是这里的难点在于如何判断型T中存在这样一个ToString方法,以便可以放入enable_if中做SFINAE。 检查类中是否存在特定成员 相同的问题在知乎上有人提出过,@孙明琦的答案提供了一个用于检测特定检测子U在型T下是否有效的检测器is_detected_v。其中用到了一个C++17的std::void_t,考虑到目前C++17还没得用,这个实现只作参考之用(事实上C++
其实我们从直观上可以很好的理解静态成员函数不能调用非静态成员变量这句话因为无论是静态成员函数还是静态成员变量,它们 都是在的范畴之的,及在的整个生存周期里始终只能存在一份。然而非静态成员变量和非静态成员函数是针对的对象而言。 然而从本质上来说的静态成员函数的函数形参中没有默认的this指针,导致不能调用具体实例对象的成员。 下面我们来测试一下: 先在静态成员函数中调用静态成员变量: #include <iostream> using namespace std; class vpoet public: static int a; int b; public:
本文针对C++函数模板与模板进行了较为详尽的实例解析,有助于帮助读者加深对C++函数模板与模板的理解。具体内容如下: 泛型编程(Generic Programming)是一种编程范式,通过将型参数化来实现在同一份代码上操作多种数据型,泛型是一般化并可重复使用的意思。泛型编程最初诞生于C++中,目的是为了实现C++的STL(标准模板库)。 模板(template)是泛型编程的基础,一个模板就是一个创建或函数的蓝图或公式。例如,当使用一个vector这样的泛型型或者find这样的泛型函数时,我们提供足够的信息,将蓝图转换为特定或函数。 一、函数模板 一个通用的函数模板(functi
template<typename T, uint32_t (T::*)(uint32_t) const = &T::Func> static constexpr bool Check(T *) { return true; } static constexpr bool Check(...) { return false; }
### 回答1: C++中的非静态成员引用必须与特定对象相对,意思是说,非静态成员变量和非静态成员函数都是属于的实例对象的,而不是属于整个的。因此,在使用非静态成员时,必须先创建一个的实例对象,然后通过该对象来访问其非静态成员。这样才能确保引用的是特定对象的成员,而不是整个的成员。 ### 回答2: C++是一种面向对象的编程语言,其中是我们定义型的基本单位,对象则是根据定义创建的实体。非静态成员是类中的一种成员变量或成员函数,只有在创建对象之后才能使用。这意味着,我们必须在对象上使用非静态成员,才能访问或修改它们的值。 因此,非静态成员引用必须与特定对象相对。这意味着,非静态成员函数或成员变量只能在创建对象之后使用,并且只能通过对象的引用或指针来使用。当我们创建一个对象时,该对象会占用内存,并被赋予一个地址,我们可以使用该地址来访问对象中的成员。 例如,如果我们定义了一个叫做Person,其中包含了一个非静态成员变量叫做name,那么在创建一个Person对象后,我们必须使用该对象的名称才能访问该变量。以下是示例代码: class Person { public: string name; int main() { Person p; p.name = "Tom"; cout << p.name << endl; return 0; 在上面的代码中,我们创建了一个Person对象,并给该对象的name成员变量赋值为"Tom"。我们可以使用p.name来访问该变量的值,因为它是非静态成员,必须特定对象相对。 总之,非静态成员引用必须与特定对象相对是C++面向对象编程的基本原则。只有在创建对象之后,我们才能使用非静态成员来访问或修改其值,并且只能使用对象的引用或指针来访问。这是确保类中的成员访问安全和良好封装的有效方法。 ### 回答3: c++中静态成员是与相关联的,而非静态成员则是与的实例相关联的。这就意味着非静态成员引用必须与特定对象相对。 在类中定义非静态成员时,它们是不能够直接被使用的。必须先创建一个的实例,然后通过该实例来访问其中的非静态成员。例如: #include <iostream> using namespace std; class MyClass { public: int myNum; //非静态成员变量 void myFunction() { cout << "Hello World!"; } //非静态成员函数 int main() { MyClass myObj; // 创建一个MyClass的实例 myObj.myNum = 15; // 访问myNum成员变量并为其赋值 cout << myObj.myNum; // 访问myNum成员变量并输出其值 myObj.myFunction(); // 调用myFunction成员函数 return 0; 在上面的例子中,我们创建了一个名为myObj的MyClass的实例,并访问了它的myNum成员变量和myFunction成员函数。如果我们没有创建实例就直接使用这些非静态成员,编译器就会报错,因为非静态成员引用必须与特定对象相对。 需要注意的是,静态成员是例外。可以在没有创建的实例的情况下,直接使用的静态成员。例如: #include <iostream> using namespace std; class MyClass { public: static int myStaticNum; // 静态成员变量 int MyClass::myStaticNum = 0; // 在外初始化静态成员变量 int main() { MyClass::myStaticNum = 15; // 直接使用静态成员变量并为其赋值 cout << MyClass::myStaticNum; // 直接输出静态成员变量的值 return 0; 在上面的例子中,我们定义了一个静态成员变量myStaticNum,然后在主函数中直接使用它进行赋值和输出。因为静态成员是与相关联的,所以它们可以在没有创建的实例的情况下进行操作。
ViolinLeeChan: 中断解释这里有个建议:non-interrupted event翻译为“非中断事件”、interrupted event叫"中断事件"合理些。另外就是对中断机制的解释搞反了,中断事件是会导致原始活动被中断的,就是被cancel掉,非中断事件才不会导致原始活动被中断。 可参考:https://www.modernanalyst.com/Careers/InterviewQuestions/tabid/128/ID/2555/What-is-the-difference-between-an-interrupting-event-and-non-interrupting-event-in-BPMN.aspx WorkFlow:BPMN 2.0介绍(四):事件(Event) netyeaxi: 你说的对,我改一下