C++使用std::enable_if作为类的成员函数选择开关

经常会碰到这样的需求,有一个类A,A的实例调用A的大部分函数都是一样的,只有小部分需要根据实例的具体情况展现出不同的表现。比如下面例子

class A
{
	void foo(){};  // do sth 
	void foo(){}; // do sth else
	//... A类的一些其他成员方法
}

A a1;
A a2;

现在我希望a1和a2对A的所有其他函数调用表现相同,而只有调用foo()时有不同的表现。我们当然可以直接把所有代码复制粘贴写第二个类,但是那样两个类将有大量相同的代码。

方法一

一种比较low的方法可能是给类搞一个类成员的bool,来控制代码执行区域

class A
{
public:
	A(bool flag) : _flag(flag){};
	
	void foo(){
		if(_flag){
			// do sth ...
		}
		else{
			// do sth else...
		}
	}; 
	
	//... A类的一些其他成员方法
	
privare:
	bool _flag;
}

这么做可以实现我们的功能,但是代码在运行期才会确定要跑哪块,而且一看就很丑,不是production code的质量。

方法二

上面的写法实在太丑,有没有什么让它变好看一点的办法呢。也许你已经想到了用模板和函数重载来解决

template<bool B>
class A
{
public:
	void foo(){ foo(std::bool_constant<B>()) };  // function selection
	
	//... A类的一些其他成员方法
	
private:
	void foo(std::true_type){  }; // do sth
	void foo(std::false_type){  }; // do sth else
}

这里相当于留一个对外的接口来调用函数,然后利用函数重载,根据std::bool_constant<>()的值来决定执行的对象。但是这样要求我们一个函数写三次。

方法三

有没有什么更好的方法呢?答案是用模板元编程。具体来讲是用std::enable_if。
std::enable_if是C++11里的东西,简单来说它可以对模板参数进行判别,根据判别结果来决定函数的选择。

std::enable_if的源码非常简单,就四行。它本质上就是一个struct,其中第一个就是普通的模板结构体定义,而第二个定义是对第一个结构体定义的偏特例化重载。std::enable_if接受两个模板参数(其实一个也可以,第二个模板参数默认是void,可以看源码),其中第一个是一个boolean。构造时当第一个模板参数为false时没有特例化,实例化为第一个结构体,而为true时则特例化为第二个结构体。两个结构体相差了一个type的定义。enable_if<true, T>::type成立,且为类型T,而enable_if<flase, T>::type未定义,因此不存在。

template<bool B, class T>
struct enable_if {}; 
  
template<class T>
struct enable_if<true, T> { using type = T; };

更多关于enable_if和偏特例化的信息,有一篇很好的帖子,可以自行查看: https://medium.com/@sidbhasin82/c-templates-what-is-std-enable-if-and-how-to-use-it-fd76d3abbabe

再回到最初的问题上来。有了std::enable_if,事情就可以放到编译期去完成了。

template<bool _B>
class A
{
	template<bool B = _B>
	typename std::enable_if<B, void>::type foo(){};  // do sth 
	
	template<bool B = _B>
	typename std::enable_if<!B, void>::type foo(){};  // do sth else
	
	//... A类的一些其他成员方法
}

A<true> a1;
A<false> a2;

在编译实例的foo()方法的时候,enable_if将根据模板参数B是true还是false来确定是否有enable_if::type的定义,如果有就可以正常执行函数,如果没有就会把根据SFINAE机制把这块内容抛弃掉。

下面是一个mini demo

template <bool _B>
class A
{
public:
  template <bool B = _B>
  A(typename enable_if<B>::type* = 0)
  {
    cout << "first constructor" << endl;
  };

  template <bool B = _B>
  A(typename enable_if<!B>::type* = 0)
  {
    cout << "second constructor" << endl;
  };


  template <bool B = _B>
  typename std::enable_if<B, void>::type foo()
  {
    cout << "first foo()" << endl;
  }

  template <bool B = _B>
  typename std::enable_if<!B, void>::type foo()
  {
    cout << "second foo()" << endl;
  }
};


int main()
{
  A<true> a1{}; // first constructor
  a1.foo();     // first foo()

  A<false> a2{}; // second constructor
  a2.foo();      // second foo()

  return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
`std::enable_shared_from_this` 是一个模板,其目的是为了解决在一个对象中保存 shared_ptr 的问题。它是 C++11 引入的一个特性。 在使用 `std::shared_ptr` 时,我们往往需要在对象中保存一个 `std::shared_ptr` 的副本,这样才能确保对象在使用完毕后不会被提前销毁。但是这种方式会导致一些问题,比如我们无法防止用户直接使用裸指针来操作对象,从而导致对象被提前销毁等问题。 这时候,我们可以使用 `std::enable_shared_from_this` 来解决这些问题。具体而言,我们需要继承 `std::enable_shared_from_this`,然后在对象中使用 `shared_from_this()` 方法来获取一个指向当前对象的 `std::shared_ptr`。 下面是一个示例代码: ```c++ #include <iostream> #include <memory> class MyClass : public std::enable_shared_from_this<MyClass> { public: std::shared_ptr<MyClass> get_shared_ptr() { return shared_from_this(); } }; int main() { std::shared_ptr<MyClass> p(new MyClass); std::shared_ptr<MyClass> q = p->get_shared_ptr(); std::cout << "p.use_count() = " << p.use_count() << std::endl; std::cout << "q.use_count() = " << q.use_count() << std::endl; return 0; } ``` 在这个示例中,我们定义了一个名为 `MyClass` 的,并且继承了 `std::enable_shared_from_this`。然后,我们在中定义了一个名为 `get_shared_ptr()` 的方法,该方法使用了 `shared_from_this()` 方法来获取一个指向当前对象的 `std::shared_ptr`。在 `main()` 函数中,我们先创建了一个 `std::shared_ptr` 对象 `p`,然后通过 `p` 调用 `get_shared_ptr()` 方法获取了一个指向同一个对象的 `std::shared_ptr` 对象 `q`。最后,我们输出了 `p` 和 `q` 的引用计数,可以看到它们的引用计数都是 2。 需要注意的是,在使用 `std::enable_shared_from_this` 时,我们需要确保对象已经被一个 `std::shared_ptr` 管理,否则使用 `shared_from_this()` 方法会导致程序崩溃。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值