C++ -Weffc++ 警告与指针
C++ -Weffc++ warning with pointers
我很难理解这个错误。
我正在使用 -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
这是警告,不是错误。在拥有默认构造函数时,您有一些指针不会正确处理。如果您希望警告消失,请定义构造函数和赋值运算符。 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
};
如果不这样做,移动和复制 class 的实例可能会对默认实例化的 constructors/assignment 运算符造成不良影响,例如 copying/moving 无法 [=18] 的资源=].看看析构函数,想想如果让默认方法处理指针会发生什么。
使用 B
时,编译器可以检测到可能违反了三规则并发出 Effective 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
一样在没有 Effective C++ 警告的情况下进行编译。很长一段时间我都无法解决这个问题。这提出了一个重要的观点。警告是由实施者的恩典给出的。如果事情很难或不可能证明,将不会有警告。
但为什么这个警告很难?
编译器必须知道寻找潜在的问题。这意味着它将寻找问题的特征。这意味着一个或多个可能需要特殊处理的成员、析构函数、复制构造函数或赋值运算符,至少没有三规则要求的其他两个特殊成员函数之一。
我怀疑 GCC 在找到至少一个特殊成员函数但不是全部时会触发 Effective C++ 警告。
让我们看看三个classes的析构函数。
A
的 int
不需要特殊的销毁逻辑。 C
的引用也没有。 B
的 vector
是另一回事。至少它需要释放一些存储空间。这需要编译器生成一些代码,一旦有一个多于一个什么都不做的析构函数,编译器就可以看到 class 有一个没有三规则的其他两个部分的析构函数,并且包含可能需要的成员特殊处理(指针)。
所以
struct C
{
C() :second(dummy)
{
}
~C() // force a destructor
{
}
int * first = nullptr;
std::vector<int>& second;
};
应该并且确实会引发有效的 C++ 警告。
注意:
生成的非常简单的复制构造函数
C c;
C d(c);
似乎不会自行触发警告。也不提供复制构造函数。警告的挂钩可能仅在析构函数上,导致警告仅在实现者的恩典下才存在的警告。
我很难理解这个错误。
我正在使用 -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
这是警告,不是错误。在拥有默认构造函数时,您有一些指针不会正确处理。如果您希望警告消失,请定义构造函数和赋值运算符。 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
};
如果不这样做,移动和复制 class 的实例可能会对默认实例化的 constructors/assignment 运算符造成不良影响,例如 copying/moving 无法 [=18] 的资源=].看看析构函数,想想如果让默认方法处理指针会发生什么。
使用 B
时,编译器可以检测到可能违反了三规则并发出 Effective 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
一样在没有 Effective C++ 警告的情况下进行编译。很长一段时间我都无法解决这个问题。这提出了一个重要的观点。警告是由实施者的恩典给出的。如果事情很难或不可能证明,将不会有警告。
但为什么这个警告很难?
编译器必须知道寻找潜在的问题。这意味着它将寻找问题的特征。这意味着一个或多个可能需要特殊处理的成员、析构函数、复制构造函数或赋值运算符,至少没有三规则要求的其他两个特殊成员函数之一。
我怀疑 GCC 在找到至少一个特殊成员函数但不是全部时会触发 Effective C++ 警告。
让我们看看三个classes的析构函数。
A
的 int
不需要特殊的销毁逻辑。 C
的引用也没有。 B
的 vector
是另一回事。至少它需要释放一些存储空间。这需要编译器生成一些代码,一旦有一个多于一个什么都不做的析构函数,编译器就可以看到 class 有一个没有三规则的其他两个部分的析构函数,并且包含可能需要的成员特殊处理(指针)。
所以
struct C
{
C() :second(dummy)
{
}
~C() // force a destructor
{
}
int * first = nullptr;
std::vector<int>& second;
};
应该并且确实会引发有效的 C++ 警告。
注意:
生成的非常简单的复制构造函数C c;
C d(c);
似乎不会自行触发警告。也不提供复制构造函数。警告的挂钩可能仅在析构函数上,导致警告仅在实现者的恩典下才存在的警告。