2015-10-27 79 views
1

在C++ 11具有保护类,它负责调用的范围出口的一些成员函数:专营成员指针模板参数分辨率

template <class T, void (T::*op)()> 
struct Guard 
{ 
    Guard(T*g): 
     _g(g){} 
    ~Guard() 
    { 
     (_g->*op)(); 
    } 
    T*_g; 
}; 

用法很简单:

typedef Guard<Foo, &Foo::bar> FooGuard; 
... 
FooGuard g(&foo); 

我的问题起源于现有的shared_ptr<Foo>。如何创建专业化,保持的T*shared_ptr<T>代替

我已经尝试过:

template <class T, void (T::*op)()> 
struct Guard<std::shared_ptr<T>, op> 
{ 
    Guard(std::shared_ptr<T>& g): 
     _g(g){} 
    ~Guard() 
    { 
     ((*_g).*op)(); 
    } 

    std::shared_ptr<T> _g; 
}; 

但是在编译期间G<std::shared_ptr<Foo>, &Foo::bar> g2(foo);有可预见的了:

错误C2440: '专业化':不能从'重载函数'转换为'void(__thiscall std :: shared_ptr :: *)(void)'

+0

'_g-> OP();'是否行得通呢? –

+0

@DavidHaim在哪里? – Dewfy

+0

在析构函数中 –

回答

1

作为@PiotrSkotnicki已经指出,你的专业化是无效的。 您可以使用类似以下,但接口不看,那该多好:

template <class T, class U, void (U::*op)()> 
struct Guard 
{ 
    Guard(T*g): 
     _g(g){} 
    ~Guard() 
    { 
     std::cout << "normal guard" << std::endl; 
     (_g->*op)(); 
    } 
    T*_g; 
}; 


template <class T, class U, void (U::*op)()> 
struct Guard<std::shared_ptr<T>, U, op> 
{ 
    Guard(std::shared_ptr<T>& g): 
     _g(g){} 
    ~Guard() 
    { 
     std::cout << "shared_ptr guard" << std::endl; 
     ((*_g).*op)(); 
    } 

    std::shared_ptr<T> _g; 
}; 

演示:

struct Foo 
{ 
    void bar() 
    { 
     std::cout << "Foo::bar()" << std::endl; 
    } 
}; 

int main() 
{ 
    Foo foo; 
    { 
     typedef Guard<Foo, Foo, &Foo::bar> FooGuard; 
     FooGuard g(&foo); 
    } 

    std::shared_ptr<Foo> foo_ptr = std::make_shared<Foo>(); 
    { 
     typedef Guard<std::shared_ptr<Foo>, Foo, &Foo::bar> FooGuard; 
     FooGuard g(foo_ptr); 
    } 

    return 0; 
} 

输出:

normal guard 
Foo::bar() 
shared_ptr guard 
Foo::bar() 

live example

1

我会建议使用lambda函数,而不是和捕获的值:

#include <functional> 
#include <memory> 

struct Guard 
{ 
    typedef std::function<void()> func_type; 

    Guard(const func_type& f): func(f) {} 
    ~Guard() { if (func) func(); } 

    func_type func; 
}; 

struct Foo 
{ 
    void bar() {} 
}; 

int main() 
{ 
    auto foo_ptr = std::make_shared<Foo>(); 
    Guard g([foo_ptr](){ foo_ptr->bar(); }); // note the capture by value 
} 

请注意,您可以自由通过参考捕捉过,如果你想守护的堆栈例如在分配的一个实例。

+0

好吧,解决方案是敏捷的,但我需要关心每个'Guard'创建的清理操作,而不是定义一次 – Dewfy

+0

@Dewfy不知道我理解你的评论;如何使用指针和部分模板专业化比这更清洁? – Sheljohn

1

如果你真的要坚持丑陋的方式,我会建议使用删除器:

#include <string> 
#include <memory> 
#include <cstdio> 

template < class T, void (T::*op)() > 
struct Guard 
{ 
    Guard(T *p): ptr(p, [](T*){}) {} 
    Guard(const std::shared_ptr<T>& p): ptr(p) {} 

    ~Guard() { if (ptr) ((*ptr).*op)(); } 

    std::shared_ptr<T> ptr; 
}; 

struct Foo 
{ 
    Foo(const char* n): name(n) {} 
    void bar() { printf("Hello from %s\n",name.c_str()); } 

    std::string name; 
}; 

int main() 
{ 
    auto foo1 = Foo("Bob"); 
    auto foo2 = std::make_shared<Foo>("Alice"); 

    Guard<Foo,&Foo::bar> g1(&foo1), g2(foo2); 
} 
+1

其实我想用专业化来完成这个,所以最终用户在很多情况下都有透明的守卫对象声明。这就是为什么我要接受@ m.s。最接近我的想法。但无论如何+1,因为它工作正常 – Dewfy