C ++ -Weffc ++警告与指针

时间:2018-12-21 23:53:39

标签: c++ compiler-errors compilation gcc-warning

我很难理解此错误。 我正在使用-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.00C++2a

2 个答案:

答案 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

似乎并不能单独发出警告。也没有提供副本构造函数。警告的钩子可能只在析构函数上,导致警告仅通过实现者的宽限存在。