代理对象的常量正确性

Const-correctness with proxy object

我对以下代码感到困惑,其中 class Baz 允许通过代理访问其内部数据 class 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,但实际上我并不完全清楚这意味着什么......做所有的数据成员是 'as if they are const'?有明确的参考吗?

另一个有趣的事情是,如果我使用 std::reference_wrapper 作为代理 class,那么它可以在没有 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有什么魔力?

谢谢!

it works if the FooBaz::baz is a plain Baz and not a reference:

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

std::reference_wrapper 版本没有变化,baz.get(); 仍然调用非常量 Baz::get() 和 returns std::reference_wrapper<Foo>std::reference_wrapper 有一个 converting constructor,返回的 std::reference_wrapper<Foo> 可以转换为 std::reference_wrapper<const Foo>,它存储对从 std::reference_wrapper<Foo> 获得的对象的引用(通过 conversion operator).

I must do a const_cast to dispatch to the right Baz::get() function.

是的。或 std::as_const,或使 Bar<T> 可转换为 Bar<const T>

What I understand about a const function member is that it makes the this pointer-to-const, but in fact it is not totally clear to me what it means... Do all the data member be 'as if they are const'?

是的,成员的行为就像 const

The expression E1->E2 is exactly equivalent to (*E1).E2 for built-in types; that is why the following rules address only E1.E2.

In the expression E1.E2:

  1. if E2 is a non-static data member:

    if E2 is of reference type T& or T&&, the result is an lvalue of type T designating the object or function to which E2 refers, otherwise, if E1 is an lvalue, the result is an lvalue designating that non-static data member of E1, otherwise (if E1 is an xvalue (which may be materialized from prvalue)), the result is an xvalue designating that non-static data member of E1.

    If E2 is not a mutable member, the cv-qualification of the result is the union of the cv-qualifications of E1 and E2, otherwise (if E2 is a mutable member), it is the union of the volatile-qualifications of E1 and E2;

cppreference

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

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

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

if I use a std::reference_wrapper as proxy class, then it works without the const_cast

那是因为它有一个允许从 std::reference_wrapper<T>.

构造 std::reference_wrapper<const T> 的构造函数

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