C++ 通过(const)引用向非const成员函数传递成员变量时,无警告
我刚刚遇到了一个问题:C++ 通过(const)引用向非const成员函数传递成员变量时,无警告,c++,constants,C++,Constants,我刚刚遇到了一个问题: 我不应该得到编译器警告(MSVC2013)吗 我应该学习一个新的指导方针吗 我是在做一些完全愚蠢的事吗 我在一个成员函数中,通过const&-参数将一个成员变量传递给另一个成员函数。被调用的函数是non-const。 如果我想一想,我很确定我不应该那样做 危险在于,const&参数可能会在被调用函数中被神奇地更改,因为它碰巧修改了最初传入的成员变量。它被允许,不被声明为const 代码中的示例: struct S { void caller() {
- 我不应该得到编译器警告(MSVC2013)吗
- 我应该学习一个新的指导方针吗
- 我是在做一些完全愚蠢的事吗
const&
-参数将一个成员变量传递给另一个成员函数。被调用的函数是non-const
。
如果我想一想,我很确定我不应该那样做
危险在于,const&
参数可能会在被调用函数中被神奇地更改,因为它碰巧修改了最初传入的成员变量。它被允许,不被声明为const
代码中的示例:
struct S
{
void caller()
{
called_one(this->memvar);
}
void called_one(const int& x)
{
// ...
this->memvar = 2; // changes x! That's surprising... at least for this function
// ...
}
private:
int memvar{};
}
我想最好不要再次意外地遇到那个错误。 也许这不是语言或编译器最容易防范的任务? 我也没有在一些流行的指南中遇到这个问题 更新: 也许我应该澄清一下,我不觉得这种行为奇怪。我不奇怪为什么别名变量会被更改或者其他什么。 如果答案只是集中在
调用的函数上,那就太好了_one
觉得奇怪,而不是我自己;)
更新:
。。。而caller
就是那个做错事的人。
但我认为他有机会意识到这一点(通过阅读代码)。他将别名传递给他知道被调用函数也可以修改的对象(不是通过传递的参数)。这可能导致被调用函数中出现意外行为
因此,即使在这种特殊情况下,编译器也不可能或不合理地提供帮助,我不明白为什么以指南的形式引入这种陷阱是不值得或没有用处的。您的问题相当于定义
f(int& x, const int& y) { x = 2; }
然后打电话
f(x, x);
它在没有任何警告的情况下修改第二个参数x
。您刚刚在两个参数之间创建了一个别名。因此,如果您的调用修改了第一个参数,那么它也修改了第二个参数
编译器无法知道变量之间的所有别名信息。它的工作只是为所有可能的别名提供正确的代码。由于调用尊重签名,编译器不应发出任何警告
更新:
在语言层面上没有任何指导方针可以防止
这种未定义的行为。别名可能会占用很多时间
形式。例如,函数可以修改数据结构
(树、列表、向量)由一个
它的元素。然后从根部,你可以到达
元素,并意外修改该元素或其属性
支持正如你所说,这是程序员的责任
注意这些要点
编译器可以查看参数,并检测两个参数是否位于(普通)别名中,并且具有相同的类型和不同的常量信息。但有时程序员希望这样的行为没有任何警告;例:
void mul_assign(double& x, const double& y) { x *= y; }
void sqr_assign(double& x) { mul_assign(x, x); }
您的代码是完全合法的,就像更简单的代码一样
int x = 0;
const int &x_r = x; // x_r can't be changed directly
x = 42; // also changes (the value referenced by) x_r
<>我通常认为你的代码是一个糟糕的设计(但是没有上下文,很难说清楚你为什么要这么做)。
如果要避免使用别名,只需按值传递x
(至少假设它不是更大的结构):
此代码是合法的,因为您正在修改
int
,而不是const int&
。对于常量指针,引用可以被视为一种改进的syntaxic sugar。这意味着您的代码的行为如下:
void called_one(const int *const x) // const pointer to const int
{
memvar = 2;
}
(注意:const
适用于左侧的元素,除非它位于开头-然后它适用于右侧的元素。)
x
指向memvar
。因此,当修改memvar
时,将修改x
点x
本身不被修改,也不能被修改,同样的*x
不能被修改,因为它是const
。参考资料也是如此
但是没有理由const
会使原始值本身不可修改。它只是表示您不能仅在此处使用x
修改x
另外,请注意,可以将int&
分解为const int&
,这很好。但是您也可以使用将其转换回int&
(如果您不删除初始类型中的const
s!!否则,就是UB)
void调用了一个(const int&x)
{
const_cast(x)=2;//合法(如果传递的'x'实际上是'const',则为非法)
}
您将无法修改x
,但此->memvar
未标记为const
,函数本身也未标记。所以最后一个选择,你做了一些愚蠢的事情,就是发生了什么如果我想一想,我很确定我不应该那样做。嗯,你可以。你的推理是错误的。仅此而已。memvar
不是常量,所以一切都是例外x
是一个const int&
,因此您不能通过x
@Asu修改memvar
-谢谢,这是我真正理解的第一条评论。是的,我不能写信给x
。但我能读!在名为_one的函数中,如果x
的值突然发生变化,这是令人惊讶的。“我认为最好不要再次意外地遇到这个错误”-现在有一些信息告诉我,再次这样做的可能性已经大大降低。这句话应该是“活到老学到老。”哦,不,对不起,我不觉得奇怪为什么会这样。但是你说的不是编译器的工作,程序员被迫对别名保持一个整体的看法,这是朝着正确的方向发展的。好吧,对我来说,本质是:在某些情况下,程序员实际上打算这样做。所以不应该有一个“永远不要…”的指导方针
void called_one(const int *const x) // const pointer to const int
{
memvar = 2;
}
void called_one(const int& x)
{
const_cast<int&>(x) = 2; // Legal (and illegal if the `x` passed is actually `const`)
}