C++11 如何使用PIMPL习语实现逻辑常量

C++11 如何使用PIMPL习语实现逻辑常量,c++11,constants,pimpl-idiom,C++11,Constants,Pimpl Idiom,想象一下PIMPL习惯用法的典型实现: class ObjectImpl; class Object { Object(ObjectImpl* object_impl) : _impl(object_impl); private: ObjectImpl* _impl; }; 我要寻找的是一种重用同一个实现的方法,以包装一个类型T,它是ObjectImpl或const ObjectImpl,而不是其他类型: class ObjectImpl; class Obj

想象一下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接口保持逻辑常量,这样编译器就不允许我在包装常量ObjectImpl*的对象上调用非常量方法

基本上就是这个技巧,借用了Scott Meyers的一个有效的C++书籍,但是增加了一层抽象:

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

private:
  Data* _data:
};
当然,我可以将整个类复制到一个类ConstObject中,并让它包装成一个const*对象,而不是一个Object*,但我显然是在试图防止代码重复


我也考虑过模板,但对于手头的任务来说,它们似乎有点过分了。首先,我希望T只能是ObjectImpl或const ObjectImpl。其次,当导出为DLL接口时,模板似乎与PIMPL的思想背道而驰。有更好的解决方案吗?

我建议采用以下通用设计模式。它会浪费额外的指针,但会强制要求
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;
};

我建议采用以下一般设计模式。它会浪费额外的指针,但会强制要求
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;
};
CRTP

现在我们创建两种类型:

using object = super_any< decltype(print), decltype(dance), decltype(boogie) > object;
using const_object = super_any< decltype(print), decltype(boogie) >;
使用object=super\u anyobject;
使用const_object=super_any
接下来,增强
super\u any
的功能,使其能够从需求严格较弱的源分配

我们的
对象o可以
(o->*跳舞)(
)。我们的
const_object cocan
double d=(co->*boogie)()

任何东西都可以存储在
对象
中,该对象支持
打印
布吉
舞蹈
所述的操作,以及
任何
的要求(复制、销毁、分配)。什么都可以

类似地,
const_对象
支持可以通过
print
boogie
以及复制/销毁/分配来描述的任何内容

来自
对象
常量对象
的派生类型可以轻松添加运算符重载功能

这项技术是先进的。您可以使用
boost::type_erasure
来执行此操作,可能比此草图更流畅。

CRTP

现在我们创建两种类型:

using object = super_any< decltype(print), decltype(dance), decltype(boogie) > object;
using const_object = super_any< decltype(print), decltype(boogie) >;
使用object=super\u anyobject;
使用const_object=super_any
接下来,增强
super\u any
的功能,使其能够从需求严格较弱的源分配

我们的
对象o可以
(o->*跳舞)(
)。我们的
const_object cocan
double d=(co->*boogie)()

任何东西都可以存储在
对象
中,该对象支持
打印
布吉
舞蹈
所述的操作,以及
任何
的要求(复制、销毁、分配)。什么都可以

类似地,
const_对象
支持可以通过
print
boogie
以及复制/销毁/分配来描述的任何内容

来自
对象
常量对象
的派生类型可以轻松添加运算符重载功能


这项技术是先进的。您可以使用
boost::type_erasure
来执行此操作,可能比这个草图更流畅。

我考虑过类似的事情,比如像往常一样实现对象,然后执行类ConstObject:public Object{private:const ObjectImpl*_impl;}隐藏基类成员。看起来有点黑和丑陋。您的解决方案看起来确实更好。这与指针重复。这违反了DRY,使用了资源,并打开了一大堆可能的bug。例如
objectfoo(废话);常数对象条(牛顿);const_Object&foo_const=foo;foo_const=巴只是切片
foo
并指向两个PIMPL。我考虑过一些非常类似的事情,比如像往常一样实现对象,然后执行类ConstObject:public Object{private:const ObjectImpl*_impl;}隐藏基类成员。看起来有点黑和丑陋。您的解决方案看起来确实更好。这与指针重复。这违反了DRY,使用了资源,并打开了一大堆可能的bug。例如
objectfoo(废话);常数对象条(牛顿);const_Object&foo_const=foo;foo_const=巴只是切片
foo
,并将其指向两个PIMPL。PIMPL在构造函数中没有典型的实现指针。PIMPL应该是一个实现细节,在类的接口处是不可见的。该构造函数可以是私有的,用于内部目的。在构造函数中使用实现指针的PIMPL并不典型。PIMPL应该是一个实现细节,在类的接口处是不可见的。该构造函数可能是内部专用的。我没有打扰你,但是我能问一下,会有什么问题吗?非常感谢。@AmiTavory您不能从
const ObjectImpl
构造
const object
。你缺少了一些东西(没有什么基本的),比如
操作符ConstObject
。问题的作者还担心DLL接口与模板的导出。CRTP是否也解决了这个问题?@aurelian将导出两个模板实例--
const\u Object\u helper
const\u Object\u helper
。从DLL中手动导出两个模板