-Woverloaded-virtual 带有默认的浅拷贝运算符

-Woverloaded-virtual with default shallow copy operator

提问人:emerg.reanimator 提问时间:10/28/2023 最后编辑:emerg.reanimator 更新时间:10/30/2023 访问量:92

问:

此警告由 G++ 编译器从 GCC-13 开始发出。

  • Clang++ 17 对这段代码很满意。

  • MSCV 19 产生类似的警告

(39):警告 C4263:“Derived &Derived::operator =(const Derived &)”:成员函数不覆盖任何基类虚拟成员函数
(46): 警告 C4264: 'const Parent &Parent::operator =(int &)': 没有可用于基 'Parent' 的虚拟成员函数的覆盖;功能是隐藏的
(10): 注意:参见 'Parent::operator =' 的声明
(5):注:见“父”声明

隐藏运算符也无济于事。

对我来说,这看起来像是 C++ 标准中的一个缺陷。

为什么我需要在派生类中实现赋值运算符,而不是从父类继承?


让我试着详细阐述这个话题:

  • GCC 13.x 和 MSVC 19 生成隐式定义的复制运算符,即使它们不应该这样做。
  • 派生类的隐式定义的复制运算符隐藏了基类的用户定义的赋值运算符。

已删除的复制分配运算符

隐式声明或显式默认(自 C++11 起)副本 类 T 的赋值运算符未定义(直到 C++11)定义为 如果满足以下任一条件,则删除(自 C++11 起):

...

  • T 具有引用类型的非静态数据成员。

...

我修改了代码以避免生成隐式定义的复制赋值运算符。但 GCC 和 MSVC 仍然需要它们。

为什么在这种情况下会生成隐式定义的复制赋值运算符(由某些编译器)?

为什么有些编译器声称运算符是隐藏的,即使它有不同的签名?

struct Parent
{
    Parent(int &x) : _x(x) {};
    virtual ~Parent() = default;

    virtual const Parent & operator=(int &x)
    {
        x = 0;
    return *this;
    }

    int &_x;
};

struct Derived : public Parent
{
    Derived(int &x) : Parent(x) {};
    virtual ~Derived() = default;
};

int main()
{
    int x = 0;
    Derived D1(x), D2(x);

#if (true)
    /*
    g++ 13.2: g++ --std=c++14 -o Woverloaded-virtual-test -Wall -Wextra Woverloaded-virtual-test.cpp
    https://gcc.godbolt.org/z/PGoof73Md
    __________________________________________________________________________________________________________
        source>:6:28: warning: 'virtual const Parent& Parent::operator=(int&)' was hidden [-Woverloaded-virtual=]
            6 |     virtual const Parent & operator=(int &x)
            |                            ^~~~~~~~
        <source>:15:8: note:   by 'Derived& Derived::operator=(const Derived&)'
        15 | struct Derived : public Parent
            |        ^~~~~~~
        Compiler returned: 0
    */

    /*
    mscv v19.37: /Wall /std:c++14
    https://gcc.godbolt.org/z/P3PrcTjxs
    __________________________________________________________________________________________________________
        <source>(13): warning C4626: 'Parent': assignment operator was implicitly defined as deleted
        <source>(19): warning C4626: 'Derived': assignment operator was implicitly defined as deleted
        <source>(19): warning C4263: 'Derived &Derived::operator =(const Derived &)': member function does not override any base class virtual member function
        <source>(19): warning C4264: 'const Parent &Parent::operator =(int &)': no override available for virtual member function from base 'Parent'; function is hidden
        <source>(6): note: see declaration of 'Parent::operator ='
        <source>(1): note: see declaration of 'Parent'
    */
#else
    /*
    g++ 13.2: g++ --std=c++14 -o Woverloaded-virtual-test -Wall -Wextra Woverloaded-virtual-test.cpp
    __________________________________________________________________________________________________________

        <source>:6:28: warning: 'virtual const Parent& Parent::operator=(int&)' was hidden [-Woverloaded-virtual=]
            6 |     virtual const Parent & operator=(int &x)
            |                            ^~~~~~~~
        <source>:15:8: note:   by 'Derived& Derived::operator=(const Derived&)'
        15 | struct Derived : public Parent
            |        ^~~~~~~
        <source>: In function 'int main()':
        <source>:27:10: error: use of deleted function 'Derived& Derived::operator=(const Derived&)'
        27 |     D1 = D2;
            |          ^~
        <source>:15:8: note: 'Derived& Derived::operator=(const Derived&)' is implicitly deleted because the default definition would be ill-formed:
        15 | struct Derived : public Parent
            |        ^~~~~~~
        <source>:15:8: error: use of deleted function 'Parent& Parent::operator=(const Parent&)'
        <source>:1:8: note: 'Parent& Parent::operator=(const Parent&)' is implicitly deleted because the default definition would be ill-formed:
            1 | struct Parent
            |        ^~~~~~
        <source>:1:8: error: non-static reference member 'int& Parent::_x', cannot use default assignment operator
        Compiler returned: 1
    */

    /*
    clang++ 17.0.1: clang++ --std=c++14 -o Woverloaded-virtual-test -Wall -Wextra Woverloaded-virtual-test.cpp
    https://gcc.godbolt.org/z/WzWfcqW3M
    __________________________________________________________________________________________________________

        <source>:111:8: error: object of type 'Derived' cannot be assigned because its copy assignment operator is implicitly deleted
        112 |     D1 = D2;
            |        ^
        <source>:15:18: note: copy assignment operator of 'Derived' is implicitly deleted because base class 'Parent' has a deleted copy assignment operator
        15 | struct Derived : public Parent
            |                  ^
        <source>:12:10: note: copy assignment operator of 'Parent' is implicitly deleted because field '_x' is of reference type 'int &'
        12 |     int &_x;
            |          ^
        1 error generated.
        Compiler returned: 1
    */

    /*
    msvc v19.37
    https://gcc.godbolt.org/z/r4nss37K1
    __________________________________________________________________________________________________________
        <source>(13): warning C4626: 'Parent': assignment operator was implicitly defined as deleted
        <source>(19): warning C4626: 'Derived': assignment operator was implicitly defined as deleted
        <source>(19): warning C4263: 'Derived &Derived::operator =(const Derived &)': member function does not override any base class virtual member function
        <source>(19): warning C4264: 'const Parent &Parent::operator =(int &)': no override available for virtual member function from base 'Parent'; function is hidden
        <source>(6): note: see declaration of 'Parent::operator ='
        <source>(1): note: see declaration of 'Parent'
        <source>(94): error C2280: 'Derived &Derived::operator =(const Derived &)': attempting to reference a deleted function
        <source>(19): note: compiler has generated 'Derived::operator =' here
        <source>(19): note: 'Derived &Derived::operator =(const Derived &)': function was implicitly deleted because a base class invokes a deleted or inaccessible function 'Parent &Parent::operator =(const Parent &)'
        <source>(13): note: 'Parent &Parent::operator =(const Parent &)': function was implicitly deleted because 'Parent' has a data member 'Parent::_x' of reference type
        <source>(12): note: see declaration of 'Parent::_x'
    */

    D1 = D2;
#endif

    return x;
}


此处保留原始代码仅供参考。

#include <iostream>

using namespace std;

struct Parent
{
    Parent() = default;
    virtual ~Parent() = default;

    virtual const Parent & operator=(int &x)
    {
        x = 0;
        return *this;
    }
};

struct Derived : public Parent
{
    Derived() = default;
    virtual ~Derived() = default;

#if (true)
    /* Set to false to get rid of the following warning

       g++ -o Woverloaded-virtual-test -Wall -Wextra Woverloaded-virtual-test.cpp
       or
       clang++ -o Woverloaded-virtual-test -Wall -Wextra Woverloaded-virtual-test.cpp

       main.cpp:18:28: warning: ‘virtual const Parent& Parent::operator=(int&)’ was hidden [-Woverloaded-virtual=]
          18 |     virtual const Parent & operator=(int &x)
             |                            ^~~~~~~~
       main.cpp:48:24: note:   by ‘constexpr Derived& Derived::operator=(const Derived&)’
          48 |     constexpr Derived& operator=(const Derived&) = delete;
             |                        ^~~~~~~~
     */
#else
    virtual const Parent & operator=(int &x) override
    {
        return Parent::operator=(x);
    }
#endif

    constexpr Derived& operator=(const Derived&) = delete;

    void method(int &x)
    {
        x *= 2;
        return;
    }
};

int main()
{
    cout<<"Hello World";

    Derived D;

    int x = 2;
    D.method(x);

    return x;
}
C++ clang gcc-warning

评论

3赞 teapot418 10/28/2023
Sonar认为分配操作员不应该是虚拟的。 还有更多讨论。
0赞 Toby Speight 10/28/2023
顺便说一句,在块中,您确实应该使用而不是.#elseoverridevirtual
0赞 emerg.reanimator 10/28/2023
@TobySpeight顺便说一句,在 #else 块中,您确实应该使用覆盖而不是虚拟 你是对的。可以在此处使用覆盖关键字。但它更像是编码糖。它不会导致警告消失。
0赞 emerg.reanimator 10/29/2023
@teapot418 Sonar 认为赋值运算符不应该是虚拟的 实际上,我确实想在其他一些派生类中重写这个运算符=
0赞 Toby Speight 10/29/2023
是的,这只是对代码质量的提醒;这是一个注释,因为它与错误无关。

答:

1赞 Toby Speight 10/28/2023 #1

我们有一个是否定义它,它隐藏了基类名称。Derived& operator=(const Derived&)operator=

Derived& operator=(const Derived&) = delete 不会使它像函数从未存在过一样。该名称已存在,但不再具有函数定义。

此声明仍然具有隐藏基类函数的效果。= delete

我们可以像让任何基类名称参与重载解析一样修复这个错误 - 我们在子类中再次看到它:using

    constexpr Derived& operator=(const Derived&) = delete;

    using Parent::operator=;

(我将忽略虚拟是否是一个好主意的问题......operator=()

评论

0赞 emerg.reanimator 10/28/2023
感谢您的回复!在这种情况下,警告消失了。但是,我不得不承认,这基本上是在派生类中覆盖 operator= 的简短形式。
0赞 emerg.reanimator 10/29/2023
此 = delete 声明仍然具有隐藏基类函数的效果。即使未隐藏浅拷贝运算符,也会发出警告: ' 10 |virtual const Parent & operator=(int &x)' ' |^~~~~~~~' ' 17 |struct 派生:public Parent' ' |^~~~~~~`Woverloaded-virtual-test.cpp:10:28: warning: ‘virtual const Parent& Parent::operator=(int&)’ was hidden [-Woverloaded-virtual=]Woverloaded-virtual-test.cpp:17:8: note: by ‘constexpr Derived& Derived::operator=(const Derived&)’
0赞 Toby Speight 10/29/2023
是的,因为您没有编写(或)的默认值隐藏了基类函数。同样的修复程序解决了这个问题。operator=()=delete
0赞 emerg.reanimator 10/29/2023
同样的修复程序解决了这个问题。正确,但这意味着对于每个不需要重写方法的派生类,我需要显式引用基类运算符 ()。可行的解决方案可能真的是使用其他运营商。如果我替换 with,那么警告就会消失,而无需向派生类添加额外的代码。operator=()using Parent::operator=operator=()operator<<()
0赞 Toby Speight 10/29/2023
是的,没错 - 每个派生类都需要它。与的比较是没有意义的,因为它不是按原样隐式提供的。usingoperator<<()operator=()