大约六年前,一位名叫Harri Porten的软件工程师写了这篇文章,问了一个问题,"成员函数什么时候应该有const限定符?什么时候不应该?" 我发现这是我能找到的关于这个问题的最好的写作,我最近一直在努力解决这个问题,而且我认为在大多数关于const正确性的讨论中,这个问题都没有得到很好的解释.由于当时不存在像SO这样强大的软件信息共享站点,我想在这里重新提出这个问题.
我有一个类应该为每个成员变量调用一个访问者方法.像这样的东西:
class A{
int a, b, c;
public:
void accept(Visitor &visitor){
visitor.visit(a);
visitor.visit(b);
visitor.visit(c);
}
};
Run Code Online (Sandbox Code Playgroud)
如何void accept() const在没有代码重复的情况下使用相同的代码获取方法?
重复的显而易见的解决方案是添加一个方法:
void accept(Visitor &visitor) const {
visitor.visit(a);
visitor.visit(b);
visitor.visit(c);
}
Run Code Online (Sandbox Code Playgroud)
该方法具有我想要的含义,但我想避免代码重复.拥有这两种方法的原因是能够通过"阅读"访问者读取变量并accept很好地使用该方法const.然后非const accept将可用于"写入/更新"访问者.
我现在正在玩C++和const-correctness.假设您具有以下结构
template <typename T>
struct important_structure {
public:
T* data;
int a;
important_structure(const T& el, int a);
void change();
};
template <typename T>
void important_structure<T>::change() {
//alter data field in some way
}
template <typename T>
important_structure <T>::important_structure(const T& el, int a) : data(&el), a(a) //error line {
};
int main() {
important_structure<int>* s = new important_structure<int>{5, 3};
}
Run Code Online (Sandbox Code Playgroud)
编译时std=c++11,编译器返回以下错误:
从'const int*'到'int*'的无效转换
现在,我知道这是不安全投下const int*到int*.问题是我有一个数据结构,我不想把字段data作为常量.
但是,我不想删除const构造函数中的限定符,因为我认为它为未来的开发人员提供了信息:它明确表示el不会被函数修改.仍然data可以通过其中的一些其他功能来修改该字段important_structure …
以下 C++ 代码无法编译,因为它将非常量指针传递给需要 const 指针的find()函数。
#include <map>
std::map<int*, double> mymap;
double myfind(const int * mykey)
{
return mymap.find(mykey)->second;
}
Run Code Online (Sandbox Code Playgroud)
有没有办法在不改变地图类型或使变量mykey非常量的情况下使查找工作?毕竟函数find()不会修改指向的对象,它只是比较指针。
我得到了
Error C2678 binary '*': no operator found which takes a left-hand operand of type 'const _InIt' (or there is no acceptable conversion)
Run Code Online (Sandbox Code Playgroud)
它是由 MSVC (2022, V17.1) 标头中的代码抛出的<algorithm>。
Error C2678 binary '*': no operator found which takes a left-hand operand of type 'const _InIt' (or there is no acceptable conversion)
Run Code Online (Sandbox Code Playgroud)
const由于上面一行的原因,第二行抛出了错误。
我传入的迭代器是“平面文件”上的自定义 LegacyRandomAccessIterator,它operator*看起来像这样:
template <class _FwdIt, class _Ty, class _Pr>
_NODISCARD _CONSTEXPR20 _FwdIt lower_bound(_FwdIt _First, const _FwdIt _Last, const _Ty& _Val, _Pr _Pred) {
... …Run Code Online (Sandbox Code Playgroud) 为什么 std::unique_ptr 的 'operator*' 成员函数可以标记为 const ( https://en.cppreference.com/w/cpp/memory/unique_ptr/operator *) 而像 'front()'、 'back( std::vector 中的 )'、'operator[]' 等未标记为 const?两者都返回对其管理的资源的非常量引用。
核心问题是我无法理解第一部分,即为什么我们能够将“operator*”标记为 const,即当该函数中的“*this”指针是 const 时,返回类型如何为“T&”指针(由于函数被标记为 const)?
标记为作业,因为这是一个关于期中考试的问题,我写道,我不明白答案.我被要求在以下声明中解释每个const的目的:
const char const * const GetName() const { return m_name; };
Run Code Online (Sandbox Code Playgroud)
那么,每个这些争论的解释是什么?
根据C++标准,const如果对象const本身不是,那么抛弃指针并写入对象是可以的.这样:
const Type* object = new Type();
const_cast<Type*>( object )->Modify();
Run Code Online (Sandbox Code Playgroud)
没关系,但这个:
const Type object;
const_cast<Type*>( &object )->Modify();
Run Code Online (Sandbox Code Playgroud)
是UB.
原因在于,当对象本身是const允许编译器优化对它的访问时,例如,不执行重复读取,因为重复读取对于不改变的对象没有意义.
问题是编译器如何知道实际上是哪些对象const?例如,我有一个功能:
void function( const Type* object )
{
const_cast<Type*>( object )->Modify();
}
Run Code Online (Sandbox Code Playgroud)
并将其编译为静态库,编译器不知道它将被调用的对象.
现在调用代码可以这样做:
Type* object = new Type();
function( object );
Run Code Online (Sandbox Code Playgroud)
它会很好,或者它可以做到这一点:
const Type object;
function( &object );
Run Code Online (Sandbox Code Playgroud)
它将是未定义的行为.
编译器应该如何遵守这些要求?如果不使后者工作,它应该如何使前者工作?
在我看来应该有四种变体 boost::optional
optional<Foo> =>持有一个可变的Foo,可以在初始化后重新分配
optional<Foo const> const =>持有一个const Foo,初始化后无法重新分配
optional<Foo> const =>(应该?)保持一个可变的Foo,但在初始化后不能重新分配
optional<Foo const> =>(应该?)持有一个const Foo,可以在初始化后重新分配
前2个案例按预期工作.但是对optional<Foo> constconst Foo 的解引用,并且optional<Foo const>在初始化之后不允许重新分配(如本问题所述).
const值类型的重新分配是我遇到的具体内容,错误是:
/usr/include/boost/optional/optional.hpp:486:错误:使 'const的富' 为 '这个' 的参数 '富&富::运算符=(const的富&)' 丢弃限定符[-fpermissive]
它发生在这里:
void assign_value(argument_type val,is_not_reference_tag) { get_impl() = val; }
Run Code Online (Sandbox Code Playgroud)
构造之后,实现使用赋值运算符作为参数化可选的类型.它显然不希望左手操作数是一个const值.但是为什么不能将非const可选项重置为新的const值,例如在这种情况下:
optional<Foo const> theFoo (maybeGetFoo());
while (someCondition) {
// do some work involving calling some methods on theFoo
// ...but they should only be const ones
theFoo = maybeGetFoo();
}
Run Code Online (Sandbox Code Playgroud)
一些问题:
我是否正确,希望这在概念上很好,而且无法做到这只是实施中的侥幸?
如果我不编辑boost源,那么在上面的循环中实现逻辑的干净方法是什么,而不是完全废弃boost :: optional?
如果这确实有意义并且我要编辑boost …
c++ boost const-correctness assignment-operator boost-optional