C++ C++;17';编译器是否可以通过引用来发现未定义的值?

C++ C++;17';编译器是否可以通过引用来发现未定义的值?,c++,gcc,compilation,clang,pass-by-reference,C++,Gcc,Compilation,Clang,Pass By Reference,在为一个项目制作一个小型解析器时,我不断地遇到分段错误。。。查到了一个换位错误 此代码示例不是原始代码,而是复制故障 我很惊讶,我的编译器都没有发现引用未初始化 GCC和Clang似乎都在编译它时没有警告或错误 他们不应该将对v2的引用标记为未初始化吗 我对C++和C++学习17/20是比较新的。 但我很好奇,为什么在本例中,编译器没有发现v2是未定义的,而是传递了对未定义对象的引用 #include <iostream> #include <vector> struct

在为一个项目制作一个小型解析器时,我不断地遇到分段错误。。。查到了一个换位错误

此代码示例不是原始代码,而是复制故障

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

GCC和Clang似乎都在编译它时没有警告或错误

他们不应该将对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";
}
#包括
#包括
结构A
{
使用vectorA=std::vector;
int foo;
向量体;
A()=delete;//隐式,但我们要确定!
A(int f)无例外
:foo{f},
vA{}
{}
};
A::vectorA和recurse(A::vectorA和head,int-tail)无例外
{head.emplace_back(tail);return head;}
int main()
{
//一棵树
向量v1{};
//使用递归fn将条形图转换为foos进行填充,
//一次一个酒吧
int bar{666};
向量v2{recurse(v2,bar)};
//                      ^
//输入“v2”而不是“v1”仍然可以编译!
//当然,应该读;A::向量_of_v2{recurse(v1,bar)};
//
//但是v2是…到底是什么?在它被传递到递归的点上?
//虽然它有地址和(可能)空间,但未初始化
//分配来持有它。
//没有警告?

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

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

struct foo : base {
    int x;
    foo() : base(x) {}
};
调用
base
构造函数时,
x
尚未初始化,但存储对它的引用以供以后使用是完全有效的


这只是一个例子,获取对尚未初始化对象的引用是很好的,禁止这样做需要编写非常不清楚的繁琐代码。

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

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

struct foo : base {
    int x;
    foo() : base(x) {}
};
调用
base
构造函数时,
x
尚未初始化,但存储对它的引用以供以后使用是完全有效的


这只是一个例子,获取对尚未初始化对象的引用是很好的,禁止这样做需要编写非常不清楚的繁琐代码。

相关标准规定,即使在初始化值之前,对值的引用也是有效的。同样,指向该值的指针也是有效的。在定义之前通过引用访问变量是无效的。例如,存储引用以供以后使用是有效的操作。您对编译器的期望很高。它必须检查
递归
,查看将
v2
提供给它是否有效,然后警告您是否有效。编译器正在运行“我不打算完成所有这些工作。语法是正确的,所以它将继续运行。@DavidParry,但是
v2
是参数
head
的一个完全有效的参数。如果
递归
是例如输出
&head
而不是访问
head
的成员,一切都会好的。如果您使用“几乎总是自动”约定,那么这个
autov2=A::vectorA{recurse(v2,bar)}
不会编译。不是每个人都会接受这种风格,因此这取决于您团队的德鲁塞人或您自己的风格敏感度。相关的和标准规定,即使在初始化值之前,对值的引用也是有效的。同样,指向该值的指针也是有效的。通过h定义之前的引用。例如,存储引用以供以后使用是一个有效的操作。您对编译器的期望很高。它必须检查
递归
,查看将
v2
提供给它是否有效,然后警告您是否有效。编译器不会完成所有这些工作。syntax是正确的,因此它将继续运行。@DavidParry但是
v2
是参数
head
的一个非常有效的参数。如果
递归
是例如输出
&head
而不是访问
head
的成员,则一切都会好起来。如果您使用“几乎总是自动”“约定,那么这个
autov2=A::vectorA{recurse(v2,bar)}”
不会编译。不是每个人都喜欢这种风格,所以这取决于你的团队成员或你自己的风格。这只是一段代码片段,它复制了使用CompilerExplorer产生的错误,而不是产生错误的代码…你需要20MB的内存…@DavidParry抱歉,我没有得到你的评论。我不是指y我们的代码在我的回答中…“不是出错的代码”?我们只能讨论我们在这里看到的东西,而不是只有你拥有的东西,如果你的示例与真实代码有很大不同,你应该编辑示例产生错误的错误如上所述。你不需要原始代码来理解问题。原始的变量名比本示例中使用的变量名更有指导意义e?@DavidHParray仍然不明白你想告诉我什么。正如我所理解的,你问的问题是,是否应该禁止传递统一引用,我给了你一个例子,这样做是完全正确的,禁止这样做是非常糟糕的。这一切与“需要20MB才能出错”有什么关系?@DavidHParry如果你被我对你的变量名的批评所困扰,为什么不改变它们呢?无论如何,如果这让你感觉更好的话,我会删除PS:p这只是一个代码片段,它再现了使用compileexplorer产生的错误,而不是产生错误的代码…你需要20MB的内存…@DavidHParry抱歉,我没有收到你的评论。