Вроде случайный вопрос ...
Я ищу способ выразить операцию приведения, которая использует определенный оператор экземпляра класса, из которого я выполняю приведение, и генерирует ошибку времени компиляции, если для типа не существует определенного оператора приведения. Так, например, я ищу что-то вроде:
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 <>, но это может сработать, когда вы хотите, чтобы он потерпел неудачу (то есть: компилятор выбирает оператор, которому разрешено преобразовывать в тип, который вы просили, когда вам нужен сбой, потому что этот оператор отсутствует).
По общему признанию, это редкий случай, но меня раздражает то, что я не могу точно выразить, что я хочу, чтобы компилятор делал в инкапсулированной функции ... отсюда и вопрос.
Использование конвертирующего конструктора с пометкой явный - это то, как вы предотвратите возможность компилятора неявно преобразованных типов инициализировать ваш класс-оболочку.
Вы имеете в виду, что хотите изменить поведение приведения по умолчанию между двумя внутренними типами?
Нет, я конвертирую класс-оболочку во внутренний тип, поэтому я не могу сказать компилятору, что я хочу, чтобы внутренний тип создавался только явно (и это было бы плохо в общем смысле).
звучит так, будто вам нужна специализация шаблона, что-то вроде этого:
/* 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; }
Обновлено: Как отмечалось в другом сообщении, вы можете добавить что-нибудь в общую версию, чтобы получить более полезное сообщение об ошибке, если выполняется неподдерживаемое приведение.
Поскольку сообщения об ошибках компилятора, связанные с шаблоном, обычно сложно распутать, если вы не против указать каждое преобразование, вы можете заставить компилятор выдавать более информативное сообщение в случае сбоя, указав также определение шаблона по умолчанию. При этом используется тот факт, что компилятор будет пытаться скомпилировать только тот код в шаблонах, который действительно вызывается.
#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;
}
Это сработает, но единственным недостатком будет необходимость явного определения каждого «действительного» варианта вместо написания одной общей версии. Я бы назвал эту версию allowed_cast, чтобы лучше уловить эффект.
Да, я считаю, что смысловая разница допустима. Должен быть способ определить во время компиляции, применяется ли явный оператор преобразования - я могу придумать, как вы можете определить, действительно ли преобразование Любые, но явное сложнее. Я подумаю еще ...
Опубликованный вами код работает с Компилятор Cameau (что обычно является хорошим показателем того, что он действителен на C++).
Как вы знаете, действительное приведение состоит не более чем из одного определенного пользователем приведения, поэтому возможное решение, о котором я думал, было добавление другого определяемого пользователем приведения путем определения нового типа в шаблоне приведения и наличия статическое утверждение, которое не доступно из новый тип для типа результата (с использованием Boost is_convertible), однако это не делает различий между операторами приведения и конструкторами приведения (ctor с одним аргументом) и позволяет выполнять дополнительные приведения (например, от 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 для встроенных типов в исходном typedef, что отлично. Хорошая идея. :)
Да, но как насчет приведения к встроенным внутренним типам, когда я не могу добавить явный конструктор или изменить поведение преобразования?