user-defined conversion
Enables implicit conversion or explicit conversion from a class type to another type.
[edit] Syntax
Conversion function is declared like a non-static member function or member function template with no return type and with the name of the form:
operator conversion-type-id
|
(1) | ||||||||
explicit operator conversion-type-id
|
(2) | (since C++11) | |||||||
conversion-type-id is a type-id except that function and array operators []
or ()
are not allowed in its declarator (thus conversion to types such as pointer to array requires a type alias/typedef or an identity template: see below). Regardless of typedef, conversion-type-id cannot represent an array or a function type.
When such member function is declared in class X, it performs conversion from X to
conversion-type-id:
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 }
[edit] Explanation
User-defined conversion function is invoked on the second stage of the implicit conversion, which consists of zero or one converting constructor or zero or one user-defined conversion function.
If both conversion functions and converting constructors can be used to perform some user-defined conversion, the conversion functions and constructors are both considered by overload resolution in copy-initialization and reference-initialization contexts, but only the constructors are considered in direct-initialization contexts.
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 }
Conversion function to its own (possibly cv-qualified) class (or to a reference to it), to the base of its own class (or to a reference to it), and to the type void can be defined, but can not be executed as part of the conversion sequence, except, in some cases, through virtual dispatch:
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 }
It can also be called using member function call syntax:
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::operator& }
When making an explicit call to the conversion function, the type-id is greedy: it is the longest possible sequence of tokens that is a valid type id:
& x.operator int * a; // parsed as & (x.operator int*) a // not as & (x.operator int) * a
Conversion functions can be inherited and can be virtual, but cannot be static. A conversion function in the derived class does not hide a conversion function in the base class unless they are converting to the same type.
Conversion function can be a template member function, for example, std::auto_ptr<T>::operator auto_ptr<Y>. See member template and template argument deduction for applicable special rules.