通过(const)引用将成员变量传递给非const成员函数

时间:2016-10-23 12:37:35

标签: c++ const

我刚遇到一个提出问题的错误:

  • 我不应该为此获得编译器警告(MSVC2013),或
  • 我应该学习一个新的指南,或者
  • 我做的事情是完全愚蠢的吗?

我在成员函数中,并通过const& - 参数将成员变量传递给另一个成员函数。被调用的函数是非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{};
}

我认为不小心再碰到那个错误会很好。 对于语言或编译器来说,防范这种情况可能不是最简单的任务吗? 我还没有在一些流行的指南来源中遇到过这个问题。

更新:

也许我应该澄清一点,我发现这种行为并不奇怪。我不知道为什么别名变量会发生变化。 如果答案只集中在called_one发现奇怪而不是我自己的函数上,那将是很好的。)

更新:

...... caller就是那个,做错了什么。 但我认为他很有可能实现这一点(通过阅读代码)。他将别名传递给他知道被调用函数也可以修改的东西(不是通过传递的参数)。这可能会导致被调用函数内出现意外行为。

所以,即使在这种特殊情况下,编译器也无法或不合理地提供帮助,我不明白为什么以指南的形式引入这个陷阱是不值得或没用的。

3 个答案:

答案 0 :(得分:4)

您的问题等同于定义

f(int& x, const int& y) { x = 2; }

然后致电

f(x, x);

修改第二个参数x,没有任何警告。您刚刚在两个参数之间创建了一个别名。因此,如果您的调用修改了第一个参数,它也会修改第二个参数。

编译器无法知道变量之间的所有别名信息。它的工作只是为所有可能的别名提供正确的代码。由于调用尊重签名,编译器不应发出任何警告。

更新:

在语言层面没有指导方法可以防止 这种未定义的行为。别名可能会占用很多 形式。例如,函数可以修改数据结构 (树,列表,矢量)从一个主动的根源 它的元素。然后从根,你可以达到 元素和意外修改元素或更糟糕的 支持。正如你所说,这是程序员的责任 要小心这些要点。

编译器可以查看参数并检测两个参数是否属于具有相同类型和不同const信息的(普通)别名。但有时候程序员想要这样的行为而没有任何警告;例如:

void mul_assign(double& x, const double& y) { x *= y; }
void sqr_assign(double& x) { mul_assign(x, x); }

答案 1 :(得分:0)

您的代码完全合法,就像更简单的

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(至少假设它不是更大的结构):

void called_one(const int x) // x is guaranteed not to change
{ ... }

答案 2 :(得分:0)

此代码是合法的,因为您要修改downcast_mut,而不是int。对于const指针,引用可以被认为是改进的语法糖。这意味着您的代码行为如下:

const int&

(注意: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_cast将其强制转换为const int&(如果你不删除初始类型中的int& !!其他,那就是UB)

const