【问题标题】:What is the purpose of `operator auto() = delete` in C++?C++ 中的 `operator auto() = delete` 的目的是什么?
【发布时间】:2021-09-15 13:34:59
【问题描述】:

C++ 中的一个类可以定义一个或多个转换运算符。其中一些可以自动推断结果类型:operator auto。所有编译器都允许程序员将任何运算符标记为已删除,operator auto 也是如此。对于具体类型,删除意味着尝试调用此类转换将导致编译错误。但是operator auto() = delete 的目的是什么?

考虑一个例子:

struct A {
    operator auto() = delete;
};

struct B : A { 
    operator auto() { return 1; }
};

int main() {
    B b;
    A a = b;   // error in Clang
    int i = b; // error in Clang and GCC
    int j = a; // error in Clang and GCC and MSVC
}

由于编译器无法推导出结果类型,它实际上禁止从此类或派生类进行任何转换,并出现错误:

function 'operator auto' with deduced return type cannot be used before it is defined.

演示:https://gcc.godbolt.org/z/zz77M5zsx

请注意,编译器在仍然允许的转换方面略有不同(例如,GCC 和 MSVC 允许转换为基类),其中哪一个是正确的?

【问题讨论】:

  • 为澄清起见,您是否期望代码中的任何转换都不是错误?
  • 澄清一下,并非所有允许的东西都一定有用。由于其他规则,有些事情是允许的,但没有任何实际应用。在这种情况下,我不确定是否有任何有用的应用程序。但值得注意的是,被允许并不意味着有用。

标签: c++ language-lawyer conversion-operator


【解决方案1】:

但是operator auto() = delete? 的目的可能是什么

以下函数的目的是什么?

auto f() = delete;

根据语法函数[dcl.fct.def.general]/1function-definitionfunction-body可能是= delete;例如,将函数定义为已删除在语法上是有效的。

C++14 为函数引入了auto 返回类型推导,并为函数定义提供了允许的语法,根据 C++14,该语法允许显式删除具有auto 返回类型的函数。

这种极端情况是否有用并不是语言真正需要考虑的,因为引入极端情况总是要付出代价的(例如“函数定义的语法应该有auto类型推导的特殊情况”)。虽然在可以提供显式默认函数定义 ([dcl.fct.def.default]) 的位置存在限制,但同样的限制不适用于显式删除的函数定义 ([dcl.fct.def.delete])。


哪一个在这里?

A a = b;   // error in Clang

Clang 为这个初始化选择用户定义的转换函数可能是错误的。根据[dcl.init.general]/15.6/15.6.2

否则,如果初始化是直接初始化,或者如果是复制初始化,其中 源类型的 cv 非限定版本与同一类或 派生类目的地的类,考虑构造函数。 [...]

优先于/15.6.3:

否则(即,对于剩余的复制初始化情况),可以从源类型转换为目标类型或(当使用转换函数时)到其派生类的用户定义转换被枚举,如中所述[over.match.copy],通过重载决议([over.match])选择最佳的。 [...]


作为该语言的用户,提供具有auto 返回类型的函数的已删除定义可以用于在语义上标记任何人都不应提供给定函数名称的任何类型的重载.

auto f() = delete;  // never expose a valid overload for f()

// elsewhere:
int f() { return 42; }
  // error: functions that differ only in 
  //        their return type cannot be overloaded

对于用户定义转换运算符的特殊情况,可以使用显式默认的auto 返回类型的用户定义转换函数,例如在预期组合基类中,类似于 Scott Meyers C++03 使类不可复制的技巧(在 C++11 引入 = delete 之前)。

struct NoUserDefinedConversionFunctionsAllowed {
    operator auto() = delete;
};

struct S : NoUserDefinedConversionFunctionsAllowed { 
    operator int() { return 1; }  // can never be used
};

【讨论】:

  • 使用= delete 从基类继承仅对特殊成员函数有用。普通函数只是隐藏基类版本。 struct S : NoUserDefinedConversionFunctionsAllowedstruct S 一样能够定义可用的转换函数。
  • @BenVoigt 你确定吗?例如。上面的操作符 int() 是 S 是不可调用的,因为继承了毯子扣除失败操作符 auto(),afaict。
  • 为什么会被继承?
  • @BenVoigt 你试过编译这个例子吗?即使是私下继承,它也会成为查找集的一部分,并且由于在尝试考虑它是否是可行的候选者时返回类型推导失败,你会得到一个硬错误。
  • @BenVoigt 那个例子既不是运算符 auto() 也不是返回类型推导。
猜你喜欢
  • 1970-01-01
  • 2019-01-26
  • 2010-12-29
  • 2021-10-06
  • 2019-01-06
  • 2011-08-30
  • 2015-04-27
  • 2014-12-28
  • 2012-02-28
相关资源
最近更新 更多