Неявное преобразование может быть действительно полезным, когда типы семантически эквивалентны. Например, представьте две библиотеки, которые реализуют тип одинаково, но в разных пространствах имен. Или просто тип, который в основном идентичен, за исключением некоторого семантического сахара здесь и там. Теперь вы не можете передать один тип в функцию (в одной из этих библиотек), которая была разработана для использования другого, если только эта функция не является шаблоном. Если это не так, вы должны каким-то образом преобразовать один тип в другой. Это должно быть тривиально (иначе все-таки типы не так идентичны!), Но при вызове преобразования явным образом происходит раздувание вашего кода в основном бессмысленными вызовами функций. Хотя такие функции преобразования могут на самом деле копировать некоторые значения, они, по сути, ничего не делают с точки зрения «программистов» высокого уровня.
Очевидно, что конструкторы и операторы неявного преобразования могут помочь, но они вводят связь, так что один из этих типов должен знать о другом. Обычно, по крайней мере, когда речь идет о библиотеках, это не так, потому что наличие одного из этих типов делает другой излишним. Кроме того, вы не всегда можете менять библиотеки.
Теперь я вижу два варианта, как заставить неявное преобразование работать в коде пользователя:
Первый - предоставить прокси-тип, который реализует операторы преобразования и конструкторы преобразования (и присваивания) для всех задействованных типов и всегда использует это.
Второе требует минимальных изменений в библиотеках, но обеспечивает большую гибкость:
Добавьте конструктор преобразования для каждого задействованного типа, который может быть включен извне.
Например, для типа A
добавить конструктор:
template <class T> A(
const T& src,
typename boost::enable_if<conversion_enabled<T,A>>::type* ignore=0
)
{
*this = convert(src);
}
и шаблон
template <class X, class Y>
struct conversion_enabled : public boost::mpl::false_ {};
, который отключает неявное преобразование по умолчанию.
Затем, чтобы включить преобразование между двумя типами, специализируйте шаблон:
template <> struct conversion_enabled<OtherA, A> : public boost::mpl::true_ {};
и реализовать функцию convert
, которую можно найти через ADL.
Я бы лично предпочел использовать второй вариант, если нет веских аргументов против него.
Теперь к актуальному вопросу (ам): какой предпочтительный способ связать типы для неявного преобразования? Мои предложения хорошие идеи? Есть ли недостатки у любого подхода? Опасны ли такие преобразования? Если разработчики библиотек вообще предоставляют второй метод, когда есть вероятность, что их тип будет реплицирован в программном обеспечении, с которым они, скорее всего, используются (я имею в виду промежуточное программное обеспечение для 3D-рендеринга, где большинство этих пакетов реализуют 3D вектор).