2016-09-16 61 views
0

试想一个典型PIMPL方法的实现:如何实现与PIMPL方法逻辑常量性

class ObjectImpl; 

class Object 
{ 
    Object(ObjectImpl* object_impl) 
    : _impl(object_impl); 

private:  
    ObjectImpl* _impl; 
}; 

我正在寻找一种方法可以重复使用相同的实现包装类型T这两种的ObjectImpl或const ObjectImpl中,但没有别的:

class ObjectImpl; 

class Object 
{ 
    Object(T* object_impl) 
    : _impl(object_impl); 

private: 
    // T being either ObjectImpl or const ObjectImpl 
    T* _impl; 
}; 

我想要实现的是通过PIMPL接口保持逻辑常量性,使我由编译器不允许调用上的对象非const方法包装一个常量ObjectImpl中*。

它基本上只是这一招从斯科特迈尔斯有效的C++的书之一,但与抽象的添加层借:

struct SData 
{ 
    const Data* data() const { return _data; } 
    Data* data() { return _data; } 

private: 
    Data* _data: 
}; 

我当然可以在整个类复制到一个类ConstObject并将其紧裹const * Object而不是Object *,但我显然试图防止代码重复。

我也想过模板,但他们似乎有点矫枉过正。首先,我希望T只能是ObjectImpl或const ObjectImpl。其次,模板似乎与作为DLL接口导出时的PIMPL思想相矛盾。有更好的解决方案吗?

+0

对于在构造函数中使用实现指针的PIMPL没有任何典型特征。 PIMPL应该是一个实现细节,在类的接口中是不可见的。 –

+0

这个构造函数可能是私人的内部用途 –

回答

2

CRTP。

template<class Storage> 
struct const_Object_helper { 
    Storage* self() { return static_cast<D*>(this); } 
    Storage const* self() const { return static_cast<D*>(this); } 
    // const access code goes here, get it via `self()->PImpl()` 
}; 
struct const_Object: const_Object_helper<const_Object> { 
    const_Object(objectImpl const* impl):pImpl(impl) {} 
private: 
    objectImpl const* pImpl = nullptr; 
    objectImpl const* PImpl() const { return pImpl; } 
    template<class Storage> 
    friend struct const_Object_helper; 
}; 
struct Object: const_Object_helper<Object> { 
    // put non-const object code here 
    Object(objectImpl* impl):pImpl(impl) {} 
    operator const_Object() const { 
    return {PImpl()}; // note, a copy/clone/rc increase may be needed here 
    } 
private: 
    objectImpl* pImpl = nullptr; 
    objectImpl const* PImpl() const { return pImpl; } 
    objectImpl* PImpl() { return pImpl; } 
    template<class Storage> 
    friend struct const_Object_helper; 
}; 

这就是零运行时开销版本,但需要的const_Object_helperObject_helper实施暴露。因为它只涉及将内容转发到实际的impl,所以这看起来相对无害。

您可以通过用纯虚拟objectImpl const* get_pimpl() const = 0objectImpl* get_pimpl() = 0替换帮助器的CRTP部分来删除该需求,然后在派生类型中实现它们。

另外,有些疯狂,方法是使用类型擦除操作的any增强(你也想教类型擦除机制有关const,而且用较少的接口super_any可以隐式转换在没有做其他一层包装)。

这里我们定义了某些操作,比如打印,舞蹈和布吉:

auto const print = make_any_method<void(std::ostream&), true>(
    [](auto&&self, std::ostream& s) { 
    s << decltype(self)(self); 
    } 
); 
auto const dance = make_any_method<void()>(
    [](auto&&self) { 
    decltype(self)(self).dance(); 
    } 
); 
auto const dance = make_any_method<double(), true>(
    [](auto&&self) { 
    return decltype(self)(self).boogie(); 
    } 
); 

现在我们创建两种类型:

using object = super_any< decltype(print), decltype(dance), decltype(boogie) > object; 
using const_object = super_any< decltype(print), decltype(boogie) >; 

接下来,充实到super_any与分配,从源的能力严格要求较弱。

我们的object o;可以(o->*dance)()。我们const_object co;可以double d = (co->*boogie)();

任何东西都可以被存储在支持printboogiedance描述的操作的object,加any要求(复印,破坏,分配)。什么都可以。

同样,const_object支持任何可以由printboogie和复制/销毁/分配描述。

派生类型从objectconst_object可以轻松地添加运算符重载功能。

该技术是先进的。您可以使用boost::type_erasure来做到这一点,可能比这个草图更清晰。

+0

希望我不会烦你,但我可以问[这个小问题](https://ideone.com/aZR44A)会有什么问题?非常感谢。 –

+0

@AmiTavory你不能从'const ObjectImpl'构造'ConstObject'。而且你缺少像'operator ConstObject'这样的东西(没有任何基础)。就是这样。 – Yakk

+0

非常感谢 - 感谢您的时间。 –

2

我会建议以下一般设计模式。它浪费额外的指针,但将强制执行该const对象将只能够访问私有对象的const方法的要求:

class ObjectImpl; 

class const_Object { 

public: 

    const_Object(const ObjectImpl* object_impl) 
    : _impl(object_impl); 

    // Only const methods 

private:  
    const ObjectImpl* _impl; 
}; 

class Object : public const_Object 
{ 
    Object(ObjectImpl* object_impl) 
    : const_Object(object_impl), _impl(object_impl); 

    // non-const methods go here. 

private:  
    ObjectImpl* _impl; 
}; 
+0

我想过......非常相似,像实现对象照常,然后做类ConstObject:public Object {private:const ObjectImpl * _impl; }遮蔽基类成员。虽然看起来有点冒险和丑陋。你的解决方案绝对看起来更好。 – user1709708

+0

这复制了指针。这违反了DRY,使用资源,并且打开了大量可能的错误。例如'Object foo(blah); const_Object bar(newt); const_Object&foo_const = foo; foo_const = bar;'把foo'切片并指向两个pImpls。 – Yakk