编译器报告“已删除”运算符 = ,但它在那里

Compiler reports 'deleted' operator = , but it is there

提问人:Aganju 提问时间:7/20/2018 最后编辑:ildjarnAganju 更新时间:4/13/2022 访问量:151

问:

我遇到了一个令人讨厌的问题,编译器声称删除了一个,但它就在那里。经过几个小时的尝试,我产生了一个重现该问题的最小解决方案。我正在使用 MSVC 社区版 2017 15.7.5(截至今天的最新版本,2018-07-20),并将其设置为“C++17”operator=

代码不是微不足道的;这个概念是模板类 TT 用于强制在一组类中强制存在静态成员函数。这与工厂模式非常相似,只是此解决方案不创建类实例,而是报告有关类的静态详细信息。fooFn

在最后一行的赋值中报告了错误,内容如下(底部有完整的错误列表):

“错误 C2280:'C &C::operator =(const C &)':尝试引用 删除功能”

但是第 5 行定义了这个运算符,对那些装饰器吗?

赋值失败时会尝试将返回的变量赋值给类成员变量。
我以为正在产生问题,但是删除每个函数中的所有功能没有任何区别;同一行中的相同错误。
const std::vector<C>&constconst

问:为什么编译器会报告此问题,可能的修复方法是什么?
我想这一定是我想念的傻东西,但我找不到它。

#include <vector>

class C
{
public:
  C(int ii) : i(ii) {}
  C& operator=(const C&) = default;    /// HERE is the assignment operator
  const int i;
};
typedef std::vector<C> CVec;   // shorthand for a vector of C's

template <class T>   // this template forces classes F1, F2, ... to have a static member function 'foo'
class TT {
public:
  static const CVec& foo(void) { return T::foo(); }
};

class F1  // one of many Fn classes
{
public:
  static const CVec& foo(void) { static CVec cv{ C{ 1 }, C{ 2 } }; return cv; }    // static member as forced by template
  //...
};
class F2  // another one of many Fn classes
{
public:
  static const CVec& foo(void) { static CVec cv{ C{ 3 } }; return cv; }     // static member as forced by template
  //...
};

class D    // controller class
{
public:
  CVec cv;
  const CVec& bar(int z)   // function to select one of the subclasses
  {
    switch (z)
    {
      case 1: return TT<F1>::foo();
      case 2: return TT<F2>::foo();
        //...
    }
  }

  void foobar(void)  //selector (from user input)
  {
    int z = 2; // user input
    cv = bar(z);   // THIS assignment produces the error
  }
};

错误全文:

------ Build started: Project: BG, Configuration: Debug Win32 ------
bgcore.cpp
c:\program files (x86)\microsoft visual studio\2017\community\vc\tools\msvc\14.14.26428\include\xutility(2443): error C2280: 'C &C::operator =(const C &)': attempting to reference a deleted function
d:\projects\bg\core\bgcore.h(8): note: see declaration of 'C::operator ='
c:\program files (x86)\microsoft visual studio\2017\community\vc\tools\msvc\14.14.26428\include\xutility(2462): note: see reference to function template instantiation '_OutIt std::_Copy_unchecked1<_InIt,_OutIt>(_InIt,_InIt,_OutIt,std::_General_ptr_iterator_tag)' being compiled
        with
        [
            _OutIt=C *,
            _InIt=C *
        ]
c:\program files (x86)\microsoft visual studio\2017\community\vc\tools\msvc\14.14.26428\include\vector(1430): note: see reference to function template instantiation '_OutIt *std::_Copy_unchecked<_Iter,C*>(_InIt,_InIt,_OutIt)' being compiled
        with
        [
            _OutIt=C *,
            _Iter=C *,
            _InIt=C *
        ]
c:\program files (x86)\microsoft visual studio\2017\community\vc\tools\msvc\14.14.26428\include\vector(1448): note: see reference to function template instantiation 'void std::vector<C,std::allocator<_Ty>>::_Assign_range<_Iter>(_Iter,_Iter,std::forward_iterator_tag)' being compiled
        with
        [
            _Ty=C,
            _Iter=C *
        ]
c:\program files (x86)\microsoft visual studio\2017\community\vc\tools\msvc\14.14.26428\include\vector(1448): note: see reference to function template instantiation 'void std::vector<C,std::allocator<_Ty>>::_Assign_range<_Iter>(_Iter,_Iter,std::forward_iterator_tag)' being compiled
        with
        [
            _Ty=C,
            _Iter=C *
        ]
c:\program files (x86)\microsoft visual studio\2017\community\vc\tools\msvc\14.14.26428\include\vector(1471): note: see reference to function template instantiation 'void std::vector<C,std::allocator<_Ty>>::assign<C*,void>(_Iter,_Iter)' being compiled
        with
        [
            _Ty=C,
            _Iter=C *
        ]
c:\program files (x86)\microsoft visual studio\2017\community\vc\tools\msvc\14.14.26428\include\vector(1471): note: see reference to function template instantiation 'void std::vector<C,std::allocator<_Ty>>::assign<C*,void>(_Iter,_Iter)' being compiled
        with
        [
            _Ty=C,
            _Iter=C *
        ]
c:\program files (x86)\microsoft visual studio\2017\community\vc\tools\msvc\14.14.26428\include\vector(1457): note: while compiling class template member function 'std::vector<C,std::allocator<_Ty>> &std::vector<_Ty,std::allocator<_Ty>>::operator =(const std::vector<_Ty,std::allocator<_Ty>> &)'
        with
        [
            _Ty=C
        ]
d:\projects\bg\core\bgcore.h(49): note: see reference to function template instantiation 'std::vector<C,std::allocator<_Ty>> &std::vector<_Ty,std::allocator<_Ty>>::operator =(const std::vector<_Ty,std::allocator<_Ty>> &)' being compiled
        with
        [
            _Ty=C
        ]
d:\projects\bg\core\bgcore.h(22): note: see reference to class template instantiation 'std::vector<C,std::allocator<_Ty>>' being compiled
        with
        [
            _Ty=C
        ]
Done building project "BG.vcxproj" -- FAILED.
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========
C++ 模板 C++17 赋值运算符 factory-method

评论

0赞 M.M 7/20/2018
= default;可以默认为已删除的版本(在本例中也是如此)。您的类具有数据成员,因此默认赋值不起作用。您可以通过删除数据成员来修复它constconst
0赞 Aganju 7/20/2018
是的。这解决了它(让它成为答案)。但是为什么 = default 意味着删除的版本呢???这似乎没有意义。如果我删除该行,运算符将被删除,因此如果我添加它,它至少应该告诉我它不能创建一个。operator=
1赞 M.M 7/20/2018
好吧,当您尝试使用它时,它会告诉您有关问题的信息......
0赞 Eljay 7/20/2018
“为什么的可以意味着删除的版本?”因为默认版本是删除 operator=,因为该类具有 const 成员变量。=default

答:

0赞 Caleth 7/20/2018 #1

始终被允许参加特殊的会员功能。这有助于遵守 0 或 5 的规则。它向读者清楚地表明,您已经考虑了该成员,并希望采用默认行为。= default

6赞 Barry 7/20/2018 #2

以下是问题的简短版本:

class C
{
public:
  C(int ii) : i(ii) {}
  C& operator=(const C&) = default;
  const int i;
};

C a(1);
a = a; // error: use of deleted function

虽然您确实默认了该函数,但这并不意味着它一定是有效的。这只是意味着你明确默认它。默认的复制分配运算符将逐个复制分配所有子对象和成员。但是你的一个成员是一个 ,你不能复制分配它!它!const intconst

具体规则在 [class.copy.assign]/7 中:

如果 X 具有以下条件,则类 X 的默认复制/移动赋值运算符定义为已删除: [...] 非类类型(或其数组)的非静态数据成员,或 [...]const

让成员只是,你就没事了。int i

评论

0赞 Aganju 7/20/2018
是的,一旦理解了问题,这似乎是显而易见的。在添加进一步的 Fn 类后,我出现了这些问题;可能只有 F1 在身边,编译器对其进行了优化,并且永远不需要。operator=
0赞 doug 4/13/2022 #3

如前所述,不能默认由于成员是 而删除的赋值复制构造函数。这是因为无法在 c++17 或更早版本中更改对象。您可以编写赋值复制构造函数,但仍然无法复制 const 成员。const

这在 c++ 20 中发生了变化,您现在可以合法地这样做。

如果您更改

C& operator=(const C&) = default;    /// HERE is the assignment operator

C& operator=(const C& from)
{
if (this!= &from)
{
   std::destroy_at(this);
   std::construct_at(this, from);
}