C++17 编译器不应该发现对未定义值的传递引用吗?

Posted

技术标签:

【中文标题】C++17 编译器不应该发现对未定义值的传递引用吗?【英文标题】:C++17 Shouldn't compilers be spotting pass-by-reference to undefined values? 【发布时间】:2018-09-19 13:15:07 【问题描述】:

在为项目制作小型解析器时,我不断收到分段错误...追踪到换位错字。

此代码示例不是原始代码,而是重现了错误。

我很惊讶我的编译器都没有发现引用未初始化。

GCC 和 Clang 似乎都可以在没有警告或错误的情况下编译它。

他们不应该将 REFERENCE TO v2 标记为未初始化吗?

我对 C++ 比较陌生,并且正在学习 C++17/20。 但我很好奇为什么在这种情况下,编译器没有发现 v2 是未定义的,而是传递了对未定义对象的引用。

#include <iostream>
#include <vector>
struct A

    using vectorA = std::vector<A>;
    int     foo;
    vectorA vA;

    A() = delete; // Implicit but let's be certain!

    A(int f) noexcept
        : foo f,
          vA  
    
;

A::vectorA& recurse(A::vectorA& head, int tail) noexcept
head.emplace_back(tail); return head;

int main()

    // A tree of A's
    A::vectorA v1 ;

    // Fill it using recursive fn converting bars into foos,
    // a bar at a time
    int bar 666;
    A::vectorA v2 recurse(v2, bar);
    //                      ^
    // typo of 'v2' instead of 'v1' still compiles!
    // SHOULD, of course, read; A::vector_of_A v2 recurse(v1, bar);
    //
    // But v2 is... what exactly? At the point it is passed to recurse?
    // It is uninitialized although it has an address and (likely) space       
    // allocated to hold it.
    // No warning?

    std::cout << "Should this compile?\n";
    std::cout << "Oops! Segmentation Fault Guaranteed!\n";

【问题讨论】:

相关Is passing a C++ object into its own constructor legal?和Has C++ standard changed with respect to the use of indeterminate values and undefined behavior in C++14? 标准规定,即使在值初始化之前,对值的引用也是有效的。同样,指向它的指针也是有效的。只有在定义之前通过引用访问变量是无效的。例如,存储参考以供以后使用是有效的操作。 您对编译器的期望很高。它必须检查recurse,看看将v2 提供给它是否有效,如果不是,则警告您。编译器不会完成所有这些工作。语法是正确的,所以它会继续下去。 @DavidHParry 但是v2 是参数head 的一个完全有效的参数。如果 recurse 是例如输出&amp;head 而不是访问head 的成员,一切都会好起来的。 如果您使用“几乎总是自动”的约定,那么这个auto v2 = A::vectorA recurse(v2, bar); 将无法编译。不是每个人都喜欢这种风格,所以这取决于你团队的德鲁特人或你自己的风格敏感性。 【参考方案1】:

如果这不能编译,那么也不能执行以下操作,这并不少见:

struct base 
    int& x;
    base(int& x) : x(x) 
;

struct foo : base 
    int x;
    foo() : base(x) 
;

在调用base 构造函数时,x 尚未初始化,但存储对它的引用以供以后使用是完全有效的。

这只是一个示例,其中获取对尚未初始化的对象的引用就可以了,禁止这样做将需要编写非常不清楚的繁琐代码。

【讨论】:

这只是一个代码 sn-p,它重现了使用 CompilerExplorer 产生的错误,而不是产生错误的代码......你需要 20MB 的空间...... @DavidHParry 抱歉,我没有收到您的评论。我没有在我的回答中引用您的代码......“不是导致错误的代码”?我们只能讨论我们在这里看到的东西,而不是只有您拥有的东西,如果您的示例与您的真实代码有很大不同,您应该编辑示例 导致错误的故障如上所述。您不需要原始代码来理解问题。原来的变量名比本例中使用的更具指导性? @DavidHParray 仍然不明白你想告诉我什么。据我了解,您问是否应该禁止传递统一引用的问题,我给您举了一个例子,这样做是完全可以的,而禁止这样做会非常糟糕。这与“出错需要 20MB”有什么关系? @DavidHParry 如果您对我对变量名的批评感到不安,为什么不更改它们?无论如何,如果这让你感觉更好,我会删除 PS:P

以上是关于C++17 编译器不应该发现对未定义值的传递引用吗?的主要内容,如果未能解决你的问题,请参考以下文章

c# - 我应该使用“ref”通过引用方法来传递集合(例如列表)吗?

c语言局部变量可以被其他的原文件引用吗

值传递和引用传递怎么理解?

我应该返回一个通过引用传递并修改的列表吗?

在c语言中子函数引用主函数中的值和引用主函数某个值的地址的区别

undefined是啥意思啊