【问题标题】:What's the exact semantics of deleted member functions in C++11?C ++ 11中删除的成员函数的确切语义是什么?
【发布时间】:2013-10-16 10:50:00
【问题描述】:
struct A
{
    A();

    A(const A&);
    A& operator =(const A&);

    A(A&&) = delete;
    A& operator =(A&&) = delete;
};

struct B
{
    B();

    B(const B&);
    B& operator =(const B&);    
};

int main()
{
    A a;
    a = A(); // error C2280

    B b;
    b = B(); // OK
}

我的编译器是 VC++ 2013 RC。

错误 C2280: 'A &A::operator =(A &&)' : 试图引用 删除功能

我只是想知道为什么当A& operator =(A&&)被删除时编译器不尝试A& operator =(const A&);

这种行为是否由 C++ 标准定义?

【问题讨论】:

  • 你也可以复制错误信息,因为 C2280 要求每个人都用谷歌搜索。
  • @Rapptz,然而,我明确定义了 copy-ctor 和 copy-assignment。
  • @WhozCraig:“查找和重载解析发生在删除的定义被记录之前。”。 1) 重载决议选择了移动赋值 2) 注意被删除 3) 编译失败
  • @mirk 所以它被声明为声明,但直到重载决议之后才真正被删除。这可以总结一下吗?因此它匹配,因此她打破了?好的。这很有意义。

标签: c++ c++11 overloading semantics language-lawyer


【解决方案1】:
a = A(); // error C2280

右侧的表达式是临时的,这意味着它将查找operator=(A&&) 并看到它已被删除。因此错误。没有进一步的搜索。

=delete not 的意思是“不要使用我,而是使用 下一个 最好的”。它的意思是,“当你需要我时,不要使用我——而是在野外独自一人。”

这是另一个例子。如果我希望我的类X 的实例仅使用long没有其他类型(即使它转换为long!),那么我将class X 声明为:

struct X
{
     X(long arg); //ONLY long - NO int, short, char, double, etc!

     template<typename T>
     X(T) = delete;
};

X a(1);  //error - 1 is int 
X b(1L); //ok    - 1L is long

这意味着,重载决议是在编译器看到=delete 部分之前执行 - 因此会导致错误,因为selected 重载被发现已删除。

希望对您有所帮助。

【讨论】:

  • const 引用可以绑定临时对象。
【解决方案2】:

当你=delete一个函数时,你实际上是在删除它的定义

8.4.3 删除定义[dcl.fct.def.delete]

1 形式的函数定义:

attribute-specifier-seqopt decl-specifier-seqopt declarator = delete ;

被称为已删除的定义。具有已删除定义的函数也称为已删除函数

但是这样做,您也声明了该功能。引用标准 [1]:

4 已删除的函数是隐式内联的。 [ 注意:单一定义规则 (3.2) 适用于已删除的定义。 —尾注] 函数的删除定义应是函数的第一个声明 [...]

因此通过执行a = A(),编译器实际上解析为A::operator=(A&amp;&amp;),因为它已被声明(不是A::operator(const A&amp;),因为A&amp;&amp; 对r 值“更具约束力”)。然而,随着其定义被删除,该行的格式不正确。

2 隐式或显式引用已删除函数(而不是声明它)的程序是非良构的。


[1] 这里强调的句子的语气实际上是祈使句。该标准规定声明函数=deleted 必须首先出现在它的其他声明之前。但是,它仍然支持删除函数也声明函数的事实。

【讨论】:

  • +1 此外,要完成 OP 似乎想要的,他必须做的就是声明用户定义的复制构造函数/复制分配操作符成员。根据 C++§ 12.8 [class.copy],用户定义版本的所述成员(副本)的存在抑制了 move 的 default 实现(分别)。 IE。如果您同时声明两者,则只能移动 副本。如果 OP 想逃避移动,只需声明副本 only.
  • @WhozCraig: “也就是说,如果你同时声明,你只能拥有移动和复制。”....或者都不声明(假设没有成员(例如@ 987654327@) 在以编译器生成的方式出现的类中)。
  • @WhozCraig 虽然我不知道为什么 OP 不想使用移动语义。 (我对此感到内疚,事实上,我提出了一个我并不为之骄傲的问题......)
  • @Nawaz 是的。引用的部分具体参考了何时生成默认实现。还有其他条件,但该条件特别适用于 OP 的情况,其用户定义的副本成员。有趣的是,如果您在班级中声明复制为= default;,您可以压制移动并且仍然拥有默认副本,但对匹配的 move-decl 存在保持沉默。我觉得这很有趣。 (参见 C++§ 12.8, p20)。它特别指出未生成移动,因为副本已“用户声明”。有趣的是与这个问题的答案有关=P
猜你喜欢
  • 2010-09-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-01-06
相关资源
最近更新 更多