A seguir está um trecho de C++ que demonstra uma classe Foo
que agrupa a std::optional<double>
, fornecendo operadores de conversão para double
e std::optional<double>
.
#include <iostream>
#include <optional>
class Foo {
public:
operator double() const {
std::cout << "Foo::operator double()" << std::endl;
return {};
}
operator std::optional<double>() const {
std::cout << "Foo::operator std::optional<double>()" << std::endl;
return {};
}
};
int main() {
Foo foo;
std::optional<double> value;
// Outputs "Foo::operator double()"
value = foo;
}
Surpreendentemente para mim, ao atribuir uma instância de Foo
a a std::optional<double>
, o operador de conversão for double
é chamado em vez do operador de conversão for std::optional<double>
.
Quando substituo std::optional
por minha própria Optional
classe personalizada, como você pode ver abaixo, o compilador seleciona o outro operador de conversão.
#include <iostream>
template<class T>
class Optional {
public:
T val;
Optional() = default;
Optional(T) {
std::cout << "Optional(T)" << std::endl;
}
Optional(Optional&&) noexcept {
std::cout << "Optional(Optional&&)" << std::endl;
}
Optional(const Optional<T>&) {
std::cout << "Optional(const Optional&)" << std::endl;
}
Optional& operator=(Optional&&) noexcept {
std::cout << "Optional::operator=(Optional&&)" << std::endl;
return *this;
}
operator const T& () const {
std::cout << "Optional::operator const T&()" << std::endl;
return val;
}
};
class Foo {
public:
operator double() const {
std::cout << "Foo::operator double()" << std::endl;
return {};
}
operator Optional<double>() const {
std::cout << "Foo::operator Optional<double>()" << std::endl;
return {};
}
};
int main() {
Foo foo;
Optional<double> value;
// Outputs
// Foo::operator Optional<double>()
// Optional::operator=(Optional&&)
value = foo;
}
Por que o compilador seleciona um operador de conversão diferente em cada cenário?