代理对象的常量正确性



我对以下代码感到困惑,其中类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指向常量,但实际上我并不完全清楚它的含义......是否所有数据成员都"好像它们是常量"?有没有明确的参考?

另一个有趣的事情是,如果我使用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有什么魔力?

谢谢!

如果FooBaz::baz是普通Baz而不是引用,则它可以工作:

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

对于std::reference_wrapper版本,它不会改变,baz.get();仍然调用非常量Baz::get()并返回std::reference_wrapper<Foo>std::reference_wrapper有一个转换构造函数,返回的std::reference_wrapper<Foo>可以转换为std::reference_wrapper<const Foo>,它存储对从std::reference_wrapper<Foo>获得的对象的引用(通过 转换运算符)。

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

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

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

是的,成员的行为就好像他们const

.

表达式E1->E2与内置类型的表达式(*E1).E2完全相同;这就是为什么以下规则仅处理E1.E2的原因。

在表达式E1.E2中:

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

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

    如果E2不是可变成员,则结果的 cv 限定是E1E2的 cv 资格的并集,否则(如果E2是一个可变成员),它是易失性限定的并集E1E2;

— CPP 偏好

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

但恒常性仅在顶层添加。如果你通过指向const的指针访问int *member;,它会变成int *const member;,而不是const int *const member;

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

如果我使用std::reference_wrapper作为代理类,那么它无需const_cast

那是因为它有一个构造函数,允许从std::reference_wrapper<T>构造std::reference_wrapper<const T>


1是的,引用在创建后无法更改为指向其他对象,但形式上它不是conststd::is_const返回引用的false

最新更新