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