【问题标题】:"warning C4172: returning address of local variable or temporary" when returning reference to static member返回对静态成员的引用时出现“警告 C4172:返回局部变量或临时地址”
【发布时间】:2022-01-17 16:27:06
【问题描述】:

我有一个带有返回值的函数的类。由于复杂的原因,需要将该值作为const 引用返回。

(最小的工作示例包含一个int 数组,实际代码有更复杂的对象,因此参考)

class Foo
{
public:
    static constexpr const int OUT_OF_BOUNDS_VALUE = -9999;
    const int& ret(int i) const { return i < 0 || i > 4 ? OUT_OF_BOUNDS_VALUE : test[i]; }

private:
    int test[5] = {0, 1, 2, 3, 4};
};

这在 VS2015 中给了我warning C4172: returning address of local variable or temporary,它甚至不能用 GCC 编译。

  • Foo 之外添加行constexpr const int Foo::OUT_OF_BOUNDS; 可以让GCC 编译得很好。 VS2015 仍然给出警告。
  • 删除 constexpr 并从定义中拆分声明可修复警告,但我为什么必须这样做?

OUT_OF_BOUNDS 不是本地的,也不是临时的,对吧?在类定义里面定义和声明的时候没有地址吗?

实时查看警告:https://godbolt.org/z/fv397b9rr

【问题讨论】:

  • 无论如何,无法复制:godbolt.org/z/e5KoKGbdW。您使用的是哪个 gcc 版本?
  • @463035818_is_not_a_number 如问题所述,这是一个 MWE,真正的代码甚至与整数无关。另外:我正在使用 GCC 9.0.1(在 WSL 上)
  • 使用 clang、gcc 和 MSVC 编译良好 - 实时 - godbolt.org/z/8r5nM5sha - 请检查您的 minimal reproducible example
  • 使用 MSVC 编译时带有警告 - live godbolt.org/z/r69abn3rG
  • 确实。它看起来像一个神螺栓错误,它用 gcc 替换了 msvc 窗格。自己把编译器改成msvc。或者试试this link,希望能成功。

标签: c++ c++11 static constexpr


【解决方案1】:

问题是在 C++11 中,我们必须为类的 static constexpr 声明添加相应的定义数据成员。这在下面有更详细的解释:

C++11

class Foo
{
public:
    static constexpr const int OUT_OF_BOUNDS_VALUE = -9999; //THIS IS A DECLARATION IN C++11 and C++14
    //other members here
};

在上面的代码sn-p(这是C++11,C++14)中,我们有一个声明类中的静态数据成员OUT_OF_BOUNDS_VALUE。因此,在恰好一个翻译单元中,我们必须提供相应的定义。否则你会得到一个链接器错误,可以看到here

也就是说,我们应该在一个翻译单元中写:

constexpr const int Foo::OUT_OF_BOUNDS;//note no initializer

C++17

class Foo
{
public:
    static constexpr const int OUT_OF_BOUNDS_VALUE = -9999; //THIS IS A DEFINITION IN C++17
    //other members here
};

在上面的代码 sn-p(用于C++17)中,我们在类中有一个静态数据成员OUT_OF_BOUNDS_VALUE定义。因此,从 C++17 开始,我们不必在其他任何地方提供OUT_OF_BOUNDS_VALUE定义,因为我们已经在类中对其进行了定义。


您收到的 MSVC 警告似乎是一个错误。

【讨论】:

  • 严格来说,对于 OP 正在使用的 VS2015,警告是准确的,因为它不支持 C++17,因为该版本尚不存在VS 制作完成。如果这是 VS2019 或 VS2022,当且仅当它们使用 C++17 或 C++20 模式时,这将是一个错误。但当该版本的标准不受支持时,它就不会是一个错误。
  • @Mgetz VS2015 至少支持 C++11。那么它不应该像 gcc 一样给出链接器错误而不是给出警告吗?
  • 如果需要,可以通过扩展来支持这一点,它发出警告说它不可移植的事实对于未定义的行为是值得称赞的。也就是说,MS 有实施草案行为的习惯,这很可能就是这样。无论如何,从技术上讲,这不是 VS2015 中的错误。当它不是官方支持的要求时,你不能有错误。
  • @Mgetz 我想我误解了你在说什么(反之亦然)。我的观点是它与 C++17 无关。相反,它是关于 C++11 的。由于 OP 使用的是 C++11,因此 必须 给出链接器错误。(或者不是吗?)。我愿意犯错。因此,请通过引用一些官方来源来纠正我(与此含义相同)“在使用 C++11 编译 this 时,允许 VS2015 产生警告而不是链接器错误”
  • standard 中非常清楚 "只要它们不改变任何格式良好的程序的行为,符合要求的实现可能具有扩展(包括额外的库函数)。需要实现根据本文档诊断使用此类格式错误的扩展的程序。但是,这样做之后,他们可以编译和执行此类程序。"
【解决方案2】:

当您在函数中使用constexpr 时会发生什么情况,即会创建一个临时本地实例。您可以尝试在静态成员 OUT_OF_BOUNDS 的声明中省略 constexprstatic,这样您就有了一个可以引用的 const 对象/实例。

编辑: 如果您必须有一个静态成员,请将其声明为 const static int 并将定义 const int Foo::OUT_OF_BOUNDS = -1; 放在适当的位置。

【讨论】:

  • 那行不通 - godbolt.org/z/Y3M775Gdq
  • 对不起,我的错。您也必须删除static(我相应地编辑了答案)。解释:静态成员不能是 const。
  • 静态成员绝对可以是const。此外,我要求成员是静态的。
  • 是的,我在考虑函数。你可以声明它const static int。当然,您必须记住在类定义之后的某个位置为静态成员 (const int Foo::OUT_OF_BOUNDS = -1;) 定义。
  • 它已经在我的问题中说,这样做确实可以解决警告。问题是:为什么?我不相信使用在类内声明的静态 const 时会创建临时本地实例,但在类外声明时不会。但如果 的情况:你的来源是什么?
【解决方案3】:

test[i] 是一个int,您将它绑定到返回语句中对const int 的引用。没有?

【讨论】:

  • 是的。你想说啥?另外:这绝不是答案,应该在我的问题下发表评论。
  • 我的意思是类型延迟。这里没有有效的转换发生。无论如何,返回 int const&amp; 是没有意义的。但是,当然,我想这是可以做到的。不过,您需要 const_cast 它。否则,OUT_OF_BOUNDS 需要是 static int 类型。
  • @T.J.Evers 还有我的意思:这是暂时的。这不是一个任务。所以我不知道它是否有效。如果可以,请标记我的话,这是标准中的错误。
  • 返回一个常量引用就像是在说“这个函数返回一个整数的引用,你不能修改它”。所以返回 test[i] 是完全有效的:你返回一个对内存位置test + i 的引用并且你声明调用者(在这种情况下是Foo:ret 的调用者)不能修改那个内存。无需转换。
  • 关于返回 const int&amp; 没有意义:是的,这有点愚蠢,但这就是为什么我在示例中声明真正的代码不使用整数(但这对于例子)
猜你喜欢
  • 2019-06-15
  • 2011-04-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-02-14
  • 2013-01-30
  • 1970-01-01
相关资源
最近更新 更多