我很难理解此错误。
我正在使用-Weffc++
标志进行编译。
此结构编译良好。
struct A
{
A(){}
int * first = nullptr;
int second = 0;
};
这没有编译。
struct B
{
B(){}
int * first = nullptr;
std::vector<int> second{};
};
我得到:
prog.cc:14:8: warning: 'struct B' has pointer data members [-Weffc++]
14 | struct B
| ^
prog.cc:14:8: warning: but does not override 'B(const B&)' [-Weffc++]
prog.cc:14:8: warning: or 'operator=(const B&)' [-Weffc++]
但是这再次编译正常。
struct C
{
int * first;
std::vector<int>& second;
};
为什么我们在指针方面出错(它们在每个结构中)?
为什么添加std::vector<int>
会导致错误?
我使用了最新的gcc 9.00
和C++2a
答案 0 :(得分:1)
这是警告,不是错误。在使用默认构造函数时,您往往无法正确使用指针。如果要消除警告,请定义构造函数和赋值运算符。 The rule of three/five/zero
struct B {
int* first;
std::vector<int> second;
B() : first(nullptr), second{} {} // default
B(const B&) = delete; // copy ctor
B(B&&) = delete; // move ctor
B& operator=(const B&) = delete; // copy assignment
B& operator=(B&&) = delete; // move assignment
~B() { delete[] first; } // dtor
};
否则,默认实例化的构造函数/赋值运算符(例如,无法复制/移动的资源的复制/移动)会移动和复制类的实例,从而导致不良影响。看一下析构函数,想想如果让默认方法处理指针会发生什么情况。
答案 1 :(得分:1)
使用B
,编译器可以检测到可能违反了“三规则”,并发出“有效C”警告。从Ted Lyngmo对这个问题的回答开始,在许多其他地方都很好地解决了这个问题。
但是为什么其他两个不触发相同的警告?
C
使我们消除了一半的麻烦:无法重新分配引用成员变量,从而防止编译器生成默认的赋值运算符以引起麻烦。
C c; // uninitialized second. GCC misses this
C d;
c = d; //fails. deleted assignment operator
,但是复制构造函数仍然应该是可能的,并且是潜在的威胁。
C c; // uninitialized second. GCC misses this
C d(c); // but it does catch the uninitialized second if you do this
对C
进行修改
std::vector<int> dummy;
struct C
{
C() :second(dummy) // initialize second
{
}
int * first = nullptr;
std::vector<int>& second;
};
允许
C c;
C d(c);
像A
一样在没有有效C ++警告的情况下进行编译。很长一段时间我都无法解决这个问题。这提出了一个重要的观点。警告是由实施者的宽限期给出的。如果某事难以证明或无法证明,则不会发出警告。
但是为什么这个警告很难?
编译器必须知道寻找潜在的问题。这意味着它将寻找问题的征兆。这意味着一个或多个可能需要特殊处理的成员,一个析构函数,一个复制构造函数或一个赋值运算符,而没有“三个规则”所要求的其他两个特殊成员函数中的至少一个。
我怀疑GCC在找到至少一个特殊成员函数而非全部成员时触发了“有效C ++”警告。
让我们看一下这三个类的析构函数。
A
的{{1}}不需要特殊的销毁逻辑。 int
的引用也没有。 C
的{{1}}是另外一个故事。至少它需要释放一些存储空间。这需要编译器生成一些代码,并且一旦有一个不做任何事情的析构函数,编译器就可以看到该类具有一个析构函数,而没有Rule of Three规则的其他两个部分,并且包含可能需要特殊处理的成员(指针)。
所以
B
应该并且确实会发出有效的C ++警告。
注意:
生成的完全无关紧要的副本构造函数vector
似乎并不能单独发出警告。也没有提供副本构造函数。警告的钩子可能只在析构函数上,导致警告仅通过实现者的宽限存在。