用户定义的转换函数
目录 |
[编辑] 语法
转换函数声明为非静态成员函数或成员函数模板,没有参数,没有显式返回类型,名称形式为
operator 转换类型标识 |
(1) | ||||||||
explicit operator 转换类型标识 |
(2) | (自 C++11 起) | |||||||
explicit ( 表达式 ) operator 转换类型标识 |
(3) | (自 C++20 起) | |||||||
转换类型标识 是一个类型标识,但函数和数组运算符 []
或 ()
在其声明符中是不允许的(因此,转换为诸如数组指针的类型需要类型别名/typedef 或恒等模板:见下文)。无论 typedef 如何,转换类型标识 都不能表示数组或函数类型。
尽管用户定义的转换函数的声明中不允许返回类型,但 声明语法的 decl-specifier-seq 可以存在,并且可以包含除 类型说明符 或关键字 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& }
当显式调用转换函数时,转换类型标识 是贪婪的:它是可能形成 转换类型标识 的最长标记序列 (包括属性,如果有)(自 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 可以在 转换类型标识 中使用,表示推导的返回类型 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 | 转换函数不能指定返回类型, 但类型存在于 转换类型标识 中 |
返回类型不能在 转换函数的声明说明符中指定 |
CWG 2175 | C++11 | 不清楚 [[noreturn]] 在 operator int [[noreturn]] (); 中是否被解析为 noptr-声明符(函数声明符)或 转换类型标识 的一部分 |
它被解析为 转换类型标识 的一部分 |