Я могу найти много информации о неявном преобразовании, скажем, из int в определенный пользователем тип. то есть, если конструктор принимает в качестве параметра int и перед ним не стоит «явный», то могут происходить неявные преобразования.
Что если я хочу, чтобы мой класс неявно конвертировал в Int?
Например, какую функцию нужно добавить внутри или за пределами SimpleClass, чтобы основная функция компилировала и выводила «1» на консоль? (см. комментарии)
#include <iostream>
class SimpleClass
{
private:
int m_int;
public:
SimpleClass(int value)
: m_int(value) {}
};
int main(int argc, const char * argv[])
{
SimpleClass simpleclass(1);
int i = simpleclass; // does not complile
std::cout << i << std::endl; // should output "1" to the console
return 0;
}
Неявные преобразования могут быть определены двумя способами:
Последнее позволяет определить преобразование из типа класса в примитивный тип. Просто добавь
class SimpleClass {
// ...
operator int() const;
};
SimpleClass::operator int() const
{
return m_int;
}
Преобразование в (почти) любой тип T
может быть выполнен operator T
функция-член.
По умолчанию он вызывается неявно, и если вы объявите это const
он также может быть вызван на const
объект.
Таким образом:
struct MyType
{
operator int() const { return 1; }
};
Неявное преобразование в базовый тип позволяет свободно играть для всех встроенных операторов, включая
Так что вам лучше убедиться, что все это работает так, как вы хотите.
И это может быть много работы!
Существуют также потенциальные проблемы с разрешением перегрузки для вызовов, связанных с экземплярами вашего типа.
Короче говоря, неявное преобразование в int
или указатель, или любой встроенный тип, обычно стоит больше, чем стоит.
Исключение, в котором это может быть полезным, — это когда класс часто используется в библиотеке.
Избегайте неявного преобразования, но предлагайте явное преобразование.
Лучшее общее явное преобразование — это, IMHO, именованная функция-член.
Альтернативой является operator T
с префиксом с ключевым словом explicit
, который поддерживается для этого использования в C ++ 11 и более поздних версиях (в C ++ 03 он может использоваться только на конструкторах).
Если вы хотите вывод через <<
вести себя так, как будто выполняется неявное преобразование, а затем просто определить operator<<
,
И аналогично для других ситуаций, где неявное преобразование может показаться общим решением: просто определите, что подходит для этой конкретной ситуации, и избегайте введения общего неявного преобразования.
Чтобы обеспечить неявное преобразование во встроенный тип и при этом избежать «бесплатного для всех» для встроенных операторов, вы можете использовать преобразование шаблонизированного типа, например как это:
#include <iostream>
template< class A, class B > struct Is_t_;
template< class Type > struct Is_t_<Type, Type> { using T = void; };
template< class A, class B >
using If_is_ = typename Is_t_<A, B>::T;
struct Bad_string
{
operator const char* () const { return "666!"; }
Bad_string( char const* = 0 ) {}
};
auto operator==( Bad_string const&, Bad_string const& )
-> bool
{ return true; }
struct Good_string
{
template< class Type, class Enabled_ = If_is_<const char*, Type>>
operator Type() const { return "42 :)"; }
Good_string( char const* = 0 ) {}
};
auto operator==( Good_string const&, Good_string const& )
-> bool
{ return true; }
#if defined( DO_GOOD )
using String = Good_string;
#elif defined( DO_BAD )
using String = Bad_string;
#else
# error "Define either DO_GOOD or DO_BAD, please."#endif
auto main() -> int
{
String a, b;
(void) (a == "alfalfa"); // Errs for Bad_string
(void) (a + 1); // Compiles for Bad_string.
}
Как ни странно, когда DO_GOOD
определяется этот код вылетает компилятор Visual C ++ 2015 с обновлением 1, так называемый «ICE» (Внутренняя ошибка компилятора).
Обходной путь для этого компилятора состоит в том, чтобы вместо этого определить If_is_
как
template< class A, class B >
using If_is_ = std::enable_if_t< std::is_same<A, B>::value >;
Чтобы ваш класс был преобразован в int
, воплощать в жизнь
operator int() const
{
return m_int;
}