搜索技巧以避免在类模板中创建字段

Mic*_*ann 5 c++

我有像类一样的范围保护(这是简化的测试用例):

template<void(*close)()>
struct Guard1
{
  template<typename O>
  Guard1(O open) { open(); }

  ~Guard1() { close(); }
};

void close() { std::cout << "close g1\n"; }

int main()
{
  Guard1<close> g1 = [](){ std::cout << "open g1\n"; };
}
Run Code Online (Sandbox Code Playgroud)

我修改它,使得close表达式也可以作为lambda给出:

class Guard2
{
  std::function<void()> close;
public:

  template<typename O, typename C>
  Guard2(O open, C close) : close(close)
  {
    open();
  }

  ~Guard2() { close(); }
};


int main()
{
  Guard2 g2(
      [](){ std::cout << "open g2\n"; },
      [](){ std::cout << "close g2\n"; });
}
Run Code Online (Sandbox Code Playgroud)

但是我必须引入一个额外的字段const std::function<void()>& close;来将lambda从构造函数传递给析构函数.

有没有办法避免这个额外的字段,同时仍然保持lambda(和使用时一个很好的语法)?

Pio*_*ycz 4

由于您只想将其用作 ScopeGuard - 那么您可以确保对您的 const 引用或右值引用close()是有效的。正如其他答案一样,您需要一个成员或基类 - 但这并不是很大的区别。但是你可以将它作为 lambda 的右值引用,而不是std::function具有相当大的性能成本:

template <class Close>
class ScopeGuard {
public:
    template <typename Open>
    ScopeGuard(Open&& open, Close&& close) 
         : close(std::forward<Close>(close))
    {
        open();
    }
    ScopeGuard(ScopeGuard&& other) : close(std::move(other.close))
    {}
    ~ScopeGuard()
    {
        close();
    }
private:
    Close&& close;
};
Run Code Online (Sandbox Code Playgroud)

为了使它更容易使用 - 有这个make功能:

template <class Open, class Close>
auto makeScopeGuard(Open&& open, Close&& close)
{
    return ScopeGuard<Close>(std::forward<Open>(open), 
                             std::forward<Close>(close));
}
Run Code Online (Sandbox Code Playgroud)

及用法:

#include <iostream>
using namespace std;

int main() 
{
   int i = 0;
   auto scope = makeScopeGuard([&i]{cout << "Open " << i++ << "\n";}, 
                               [&i]{cout << "Close " << i++ << "\n";});
   cout << "Body\n";
}
Run Code Online (Sandbox Code Playgroud)

输出:

Open 0
Body
Close 1
Run Code Online (Sandbox Code Playgroud)

我验证它适用于 gcc 和 clang、C++14,没有错误/警告。