C++ 中“operator auto() = delete”的用途是什么?

Fed*_*dor 27 c++ conversion-operator language-lawyer

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
}
Run Code Online (Sandbox Code Playgroud)

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

function 'operator auto' with deduced return type cannot be used before it is defined.
Run Code Online (Sandbox Code Playgroud)

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

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

dfr*_*fri 9

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

以下函数的目的是什么?

auto f() = delete;
Run Code Online (Sandbox Code Playgroud)

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

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

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


其中哪一位在这里?

A a = b;   // error in Clang
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

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

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

struct S : NoUserDefinedConversionFunctionsAllowed { 
    operator int() { return 1; }  // can never be used
};
Run Code Online (Sandbox Code Playgroud)