Неявное преобразование из пользовательского типа в примитивный тип в переполнении стека

Я могу найти много информации о неявном преобразовании, скажем, из 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;
}

7

Решение

Неявные преобразования могут быть определены двумя способами:

  • неявный конструктор с одним аргументом.
  • неявная функция преобразования (оператор преобразования a.k.a.), N3337 12.3.2

Последнее позволяет определить преобразование из типа класса в примитивный тип. Просто добавь

class SimpleClass {
// ...
operator int() const;
};

SimpleClass::operator int() const
{
return m_int;
}
10

Другие решения

Технический.

Преобразование в (почти) любой тип 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 >;
4

Чтобы ваш класс был преобразован в int, воплощать в жизнь

operator int() const
{
return m_int;
}
1
По вопросам рекламы [email protected]