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`)
}