Есть ли способ использовать только оператор класса?
-
03-07-2019 - |
Вопрос
Какой-то случайный вопрос...
Я ищу способ выразить операцию приведения, которая использует определенный оператор экземпляра класса, из которого я выполняю приведение, и генерирует ошибку времени компиляции, если для типа не определен оператор приведения.Итак, например, я ищу что-то вроде:
template< typename RESULT_TYPE, typename INPUT_TYPE >
RESULT_TYPE operator_cast( const INPUT_TYPE& tValue )
{
return tValue.operator RESULT_TYPE();
}
// Should work...
CString sString;
LPCTSTR pcszString = operator_cast< LPCTSTR >( sString );
// Should fail...
int iValue = 42;
DWORD dwValue = operator_cast< DWORD >( iValue );
Интересная заметка:Приведенный выше код приводит к сбою компилятора VS2005 C++ и некорректно компилируется в компиляторе VS2008 C++ из-за, как я предполагаю, ошибки компилятора, но, надеюсь, демонстрирует идею.
Кто-нибудь знает какой-нибудь способ добиться такого эффекта?
Редактировать:Больше обоснования, чтобы объяснить, почему вы можете это использовать.Предположим, у вас есть класс-оболочка, который должен инкапсулировать или абстрагировать тип, и вы приводите его к инкапсулированному типу.Вы можете использовать static_cast<>, но это может сработать, если вы хотите, чтобы он потерпел неудачу (т. е.:компилятор выбирает оператор, которому разрешено преобразование в запрошенный вами тип, когда вы хотели, чтобы произошел сбой, поскольку этот оператор отсутствует).
По общему признанию, это необычный случай, но меня раздражает то, что я не могу точно выразить то, что я хочу, чтобы компилятор делал, в инкапсулированной функции...отсюда и вопрос здесь.
Решение
Код, который вы разместили, работает с Компилятор Камо (что обычно является хорошим показателем того, что это действительный C++).
Как вы знаете, допустимое приведение состоит не более чем из одного пользовательского приведения, поэтому возможным решением, о котором я думал, было добавление еще одного пользовательского приведения путем определения нового типа в шаблоне приведения и наличия статическое утверждение что приведение нового типа к типу результата недоступно (с использованием повышение is_convertible), однако это не делает различия между операторами приведения и конструкторами приведения (ктор с одним аргументом) и позволяет выполнять дополнительные приведения (например, void*
к bool
).Я не уверен, что различие между операторами приведения и конструкторами приведения является правильным. правильный нужно сделать, но именно об этом говорится в вопросе.
После пары дней размышлений до меня дошло, что можно просто взять адрес каст-оператора.Это немного легче сказать, чем сделать, из-за сложного указателя на синтаксис элементов в C++ (мне потребовалось гораздо больше времени, чем я ожидал, чтобы сделать это правильно).Не знаю, работает ли это на VS2008, проверял только на Cameau.
template< typename Res, typename T>
Res operator_cast( const T& t )
{
typedef Res (T::*cast_op_t)() const;
cast_op_t cast_op = &T::operator Res;
return (t.*cast_op)();
}
Редактировать: У меня была возможность протестировать его на VS2005 и VS2008.Мои выводы отличаются от исходного постера.
- На VS2008 исходная версия работает нормально (как и моя).
- В VS2005 исходная версия приводит к сбою компилятора только при приведении из встроенного типа (например,приведение int к int) после предоставления ошибки компиляции, которая мне тоже кажется не такой уж плохой, и моя версия, похоже, работает во всех случаях.
Другие советы
Использование конструктора преобразования, отмеченного явный это то, как вы запретите компилятору разрешать неявно преобразованным типам инициализировать ваш класс-оболочку.
Поскольку сообщения об ошибках компилятора, связанные с шаблонами, обычно сложно разгадать, если вы не против указать каждое преобразование, вы можете заставить компилятор выдавать более информативное сообщение в случае сбоя, предоставив также определение шаблона по умолчанию.При этом используется тот факт, что компилятор будет пытаться скомпилировать код только в шаблонах, которые фактически вызываются.
#include <string>
// Class to trigger compiler warning
class NO_OPERATOR_CONVERSION_AVAILABLE
{
private:
NO_OPERATOR_CONVERSION_AVAILABLE(){};
};
// Default template definition to cause compiler error
template<typename T1, typename T2> T1 operator_cast(const T2&)
{
NO_OPERATOR_CONVERSION_AVAILABLE a;
return T1();
}
// Template specialisation
template<> std::string operator_cast(const std::string &x)
{
return x;
}
звучит так, будто вам нужна специализация шаблонов, подойдет что-то вроде этого:
/* general template */
template<typename T1, typename T2> T1 operator_cast(const T2 &x);
/* do this for each valid cast */
template<> LPCTSTR operator_cast(const CString &x) { return (LPCTSTR)x; }
РЕДАКТИРОВАТЬ:Как отмечалось в другом посте, вы можете добавить что-то в общую версию, чтобы выдавать более полезное сообщение об ошибке, если выполняется неподдерживаемое приведение типов.