允许在包装模板中将非常数转换为常量

Allow a non-const to convert to const in a wrapper template

本文关键字:非常 转换 常量 包装      更新时间:2023-10-16

如何允许非常量引用在包装模板复制构造函数中转换为常量引用?请注意,我的复制构造函数是一个逻辑移动构造函数(C++11之前)——init成员跟踪当前有效的包装器。

template<typename T>
class wrap 
{
    T & object;
    bool init;
public:
    wrap( T& object ) : object(object), init( true ) { }
    //attempt which fails since "init" is private in other type
    template<typename O>
    wrap( wrap<O> const & o )
        : object( o.object )
        , init( true )
    {
        const_cast<wrap<O>&>(o).init = false;
    }
};

如果另一个类型完全相同,这也可以正常工作,因为访问规则允许访问私有init变量。基本上,以下内容应该有效:

//adding const
wrap<Type> a( get() );
wrap<Type const> b = a;
//base type would also be nice
wrap<BaseType> c = a;

Befriend其他专业:

template <typename U> friend class wrap;

或者为了更好的封装,只需要它们的转换构造函数:

template <typename U> template <typename O> 
friend wrap<U>::wrap(wrap<O> const &);

最好声明init mutable;使用const_cast,如果有人试图复制const对象,则存在未定义行为的风险。

此外,请注意,构造函数模板不会重载隐式生成的复制构造函数。您还需要一个复制构造函数来对init:执行正确的操作

wrap(wrap const & o) : object(o.object), init(true) {o.init = false;}

为了绕过访问限制,您可以简单地将wrap的其他实例添加为好友,因此在类的某个位置添加template<typename U> friend class wrap;。这样你的例子就可以编译了。

然而,你真的不应该那样对编译器撒谎。您将o作为const ref传递,并承诺不更改它,然后再更改它。这是不好的(或者标准允许的),所以编译器可以自由地以各种方式破坏代码。您确实应该使用const_cast来调用函数,这些函数将参数视为非常量,但不会更改参数本身(想到c api)。在这种情况下,我建议将构造函数声明为template<typename O> wrap(wrap<O>& o)。如果你真的想把它作为常量引用,你也可以把init声明为mutable,这意味着即使对象是const,它也可以被修改

您还应该注意,您没有声明复制构造函数(即使template<typename O> wrap( wrap<O> const & o )可以接受wrap<T>,它也不算在内,所以您也需要定义它,或者复制到同一类型将使用编译器生成的复制构造函数。