用户定义的转换函数
内容 |
[编辑] 语法
转换函数声明类似于非静态成员函数或成员函数模板,没有参数,没有显式返回类型,并且名称为以下形式
operator 转换类型标识符 |
(1) | ||||||||
explicit operator 转换类型标识符 |
(2) | (自 C++11 起) | |||||||
explicit ( 表达式 ) operator 转换类型标识符 |
(3) | (自 C++20 起) | |||||||
转换类型标识符是一个类型标识符,除了函数和数组运算符 []
或 ()
不允许在其声明符中使用(因此转换为诸如指向数组的指针之类的类型需要类型别名/typedef 或一个标识模板:见下文)。无论 typedef 如何,转换类型标识符都不能表示数组或函数类型。
虽然在用户定义的转换函数的声明中不允许返回类型,但声明语法的声明说明符序列可能存在,并且可能包含除类型说明符或关键字 static
之外的任何说明符。特别地,除了explicit
之外,说明符inline
、virtual
, constexpr
(自 C++11 起), consteval
(自 C++20 起) 和 friend
也是允许的(注意 friend
需要限定名称:friend A::operator B();)。
当这样的成员函数在类 X 中声明时,它会执行从 X 到 转换类型标识符 的转换。
struct X { // implicit conversion operator int() const { return 7; } // explicit conversion explicit operator int*() const { return nullptr; } // Error: array operator not allowed in conversion-type-id // operator int(*)[3]() const { return nullptr; } using arr_t = int[3]; operator arr_t*() const { return nullptr; } // OK if done through typedef // operator arr_t () const; // Error: conversion to array not allowed in any case }; int main() { X x; int n = static_cast<int>(x); // OK: sets n to 7 int m = x; // OK: sets m to 7 int* p = static_cast<int*>(x); // OK: sets p to null // int* q = x; // Error: no implicit conversion int (*pa)[3] = x; // OK }
[编辑] 解释
用户定义的转换函数在隐式转换的第二阶段调用,该阶段包含零个或一个转换构造函数或零个或一个用户定义的转换函数。
如果转换函数和转换构造函数都可以用于执行某些用户定义的转换,则转换函数和构造函数都会被重载解析在复制初始化和引用初始化上下文中考虑,但在直接初始化上下文中,只考虑构造函数。
struct To { To() = default; To(const struct From&) {} // converting constructor }; struct From { operator To() const {return To();} // conversion function }; int main() { From f; To t1(f); // direct-initialization: calls the constructor // Note: if converting constructor is not available, implicit copy constructor // will be selected, and conversion function will be called to prepare its argument // To t2 = f; // copy-initialization: ambiguous // Note: if conversion function is from a non-const type, e.g. // From::operator To();, it will be selected instead of the ctor in this case To t3 = static_cast<To>(f); // direct-initialization: calls the constructor const To& r = f; // reference-initialization: ambiguous }
可以定义转换为其自身(可能具有cv限定符)类(或其引用)、其基类(或其引用)以及类型void的转换函数,但不能作为转换序列的一部分执行,除了在某些情况下通过虚调度执行。
struct D; struct B { virtual operator D() = 0; }; struct D : B { operator D() override { return D(); } }; int main() { D obj; D obj2 = obj; // does not call D::operator D() B& br = obj; D obj3 = br; // calls D::operator D() through virtual dispatch }
它也可以使用成员函数调用语法调用。
struct B {}; struct X : B { operator B&() { return *this; }; }; int main() { X x; B& b1 = x; // does not call X::operatorB&() B& b2 = static_cast<B&>(x); // does not call X::operatorB& B& b3 = x.operator B&(); // calls X::operatorB& }
在显式调用转换函数时,conversion-type-id 是贪婪的:它是可能构成 conversion-type-id 的最长标记序列(包括属性,如果有的话)(自 C++11 起)
& x.operator int * a; // error: parsed as & (x.operator int*) a, // not as & (x.operator int) * a operator int [[noreturn]] (); // error: noreturn attribute applied to a type
占位符auto 可用于 conversion-type-id,表示推导的返回类型 struct X { operator int(); // OK operator auto() -> short; // error: trailing return type not part of syntax operator auto() const { return 10; } // OK: deduced return type operator decltype(auto)() const { return 10l; } // OK: deduced return type }; 注意:转换函数模板不允许具有推导的返回类型。 |
(自 C++14 起) |
转换函数可以被继承,也可以是虚函数,但不能是静态函数。派生类中的转换函数不会隐藏基类中的转换函数,除非它们转换为相同的类型。
转换函数可以是模板成员函数,例如,std::auto_ptr<T>::operator auto_ptr<Y>
。请参阅成员模板和模板参数推导以了解适用的特殊规则。
[编辑] 关键字
[编辑] 缺陷报告
以下行为更改缺陷报告被追溯应用于先前发布的 C++ 标准。
DR | 应用于 | 发布的行为 | 正确行为 |
---|---|---|---|
CWG 296 | C++98 | 转换函数可以是静态的 | 它们不能被声明为静态的 |
CWG 2016 | C++98 | 转换函数不能指定返回类型, 但这些类型存在于 conversion-type-id 中 |
返回类型不能在 转换函数的声明说明符中指定 |
CWG 2175 | C++11 | 不清楚 [[noreturn]] 在 operator int [[noreturn]] (); 中是作为 noptr-declarator(函数声明符)的一部分还是 conversion-type-id 的一部分解析 |
它被解析为 conversion-type-id |