【问题标题】:Const-correctness with proxy object代理对象的常量正确性
【发布时间】:2020-11-05 02:47:18
【问题描述】:

我对以下代码感到困惑,其中Baz 类通过代理类Bar 提供对其内部数据的访问权限:

struct Foo{};

template<class T>
struct Bar 
{
    Bar(T &val_): val(val_) {}

    T &val;
};

struct Baz 
{
    Bar<const Foo> get() const {return Bar<const Foo>(foo);}
    Bar<Foo>       get()       {return Bar<Foo>      (foo);}
    Foo foo;
};

struct FooBaz
{
    FooBaz(Baz &baz_): baz(baz_) {}
    // Bar<const Foo> get() const {return baz.get();} // this do not compile
    Bar<const Foo> get() const {return const_cast<const Baz &>(baz).get();} // the const_cast seems to be required
    Baz &baz;
};

看来我必须做一个const_cast 才能调度到正确的Baz::get() 函数。

我不明白的第一件事是,如果 FooBaz::baz 是普通的 Baz 而不是参考,它会起作用:

struct Foo{};

template<class T>
struct Bar 
{
    Bar(T &val_): val(val_) {}

    T &val;
};

struct Baz 
{
    Bar<const Foo> get() const {return Bar<const Foo>(foo);}
    Bar<Foo>       get()       {return Bar<Foo>      (foo);}
    Foo foo;
};

struct FooBaz
{
    FooBaz(Baz &baz_): baz(baz_) {}
    Bar<const Foo> get() const {return baz.get();} // Ok
    Baz baz;
};

我对 const 函数成员的理解是,它使 this 指向 const 的指针,但实际上我并不完全清楚它的含义......所有数据成员都'好像他们是常量'?有明确的参考吗?

另一个有趣的事情是,如果我使用 std::reference_wrapper 作为代理类,那么它可以在没有 const_cast 的情况下工作:

#include <functional>

struct Foo {};

struct Baz 
{
    std::reference_wrapper<const Foo> get() const {return std::cref(foo);}
    std::reference_wrapper<Foo>       get()       {return std::ref (foo);}
    Foo foo;
};

struct FooBaz
{
    FooBaz(Baz &baz_): baz(baz_) {}
    std::reference_wrapper<const Foo> get() const {return baz.get();} // Ok
    Baz &baz;
};

std::reference_wrapper 有什么魔力?

谢谢!

【问题讨论】:

    标签: c++ templates const-correctness


    【解决方案1】:

    如果FooBaz::baz 是普通的Baz 而不是参考,则它可以工作:

    这就是重点。在const 成员函数中,数据成员也被视为const。请注意,对于引用,这意味着引用成员本身被视为const,而不是被引用的对象。即baz 将被视为Baz &amp; const(常量引用),而不是Baz const &amp;(常量引用)。引用实际上不能是 const 限定的,而 const-ness 只是被忽略了,因此即使在 const 成员函数中,baz.get(); 也将始终调用非 const Baz::get()

    std::reference_wrapper 版本没有改变,baz.get(); 仍然调用非常量 Baz::get() 并返回 std::reference_wrapper&lt;Foo&gt;std::reference_wrapper 有一个converting constructor,返回的std::reference_wrapper&lt;Foo&gt; 可以转换为std::reference_wrapper&lt;const Foo&gt;,其中存储了对从std::reference_wrapper&lt;Foo&gt; 获得的对象的引用(通过 conversion operator)。

    【讨论】:

      【解决方案2】:

      我必须做一个const_cast 来调度到正确的Baz::get() 函数。

      是的。或std::as_const,或将Bar&lt;T&gt; 转换为Bar&lt;const T&gt;

      我对 const 函数成员的理解是它使 this 指针指向 const,但实际上我并不完全清楚它的含义......所有数据成员都'好像它们是常量'?

      是的,成员的行为就像const

      表达式E1-&gt;E2 完全等价于内置类型的(*E1).E2;这就是为什么以下规则只针对E1.E2

      在表达式E1.E2

      1. 如果E2 是非静态数据成员:

        如果E2 的引用类型为T&amp;T&amp;&amp;,则结果是T 类型的左值,指定E2 所引用的对象或函数, 否则,如果E1 是左值,则结果是一个左值,指定E1 的非静态数据成员, 否则(如果E1 是一个xvalue(可以从prvalue 实现)),结果是一个xvalue,指定E1 的非静态数据成员。

        如果 E2 不是可变成员,则结果的 cv 限定是 E1E2 的 cv 限定的并集,否则(如果 E2 是一个可变成员),它是 volatile-qualifications 的并集 E1E2;

      ——cppreference

      当通过指向const 类的指针访问时,其成员变为const

      但是常量只添加在顶层。如果您通过指向const 的指针访问int *member;,它将变为int *const member;,而不是const int *const member;

      引用的工作方式类似,但由于引用本身不能是const1,因此它们的类型不会改变。

      如果我使用std::reference_wrapper 作为代理类,那么它可以在没有const_cast 的情况下工作

      这是因为它有一个构造函数,允许从 std::reference_wrapper&lt;T&gt; 构造 std::reference_wrapper&lt;const T&gt;


      1 是的,引用在创建后不能更改为指向不同的对象,但形式上它不是conststd::is_const 返回 false 以供参考。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2017-12-13
        • 1970-01-01
        • 2010-09-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多