Шаблоны C++ и проблема неоднозначности
У меня есть подмножество класса указателей, которые выглядят так:
template <typename T>
struct Pointer
{
Pointer();
Pointer(T *const x);
Pointer(const Pointer &x);
template <typename t>
Pointer(const Pointer<t> &x);
operator T *() const;
};
Цель последнего конструктора - позволить передать Pointer
подкласса, или в основном любой тип, который неявно преобразуется в T *
, Это фактическое правило обеспечивается только определением конструктора, и компилятор не может понять его только с помощью объявления. Если я уроню его и попробую пройти Pointer<Sub>
конструктору Pointer<Base>
, Я получаю ошибку компиляции, несмотря на возможный путь через operator T *()
,
Хотя он решает вышеуказанную проблему, он создает еще одну. Если у меня есть перегруженная функция, чья одна перегрузка принимает Pointer<UnrelatedClass>
а другой берет Pointer<BaseClass>
и я пытаюсь вызвать его с Pointer<SubClass>
Я получаю неоднозначность между двумя перегрузками с намерением, конечно, что последняя перегрузка будет вызвана.
Какие-либо предложения? (Надеюсь, я был достаточно ясен)
2 ответа
Лекарство от вашей проблемы называется SFINAE (ошибка замены не является ошибкой)
#include "boost/type_traits/is_convertible.hpp"
#include "boost/utility/enable_if.hpp"
template<typename T>
class Pointer {
...
template<typename U>
Pointer(const Pointer<U> &x,
typename boost::enable_if<
boost::is_convertible<U*,T*>
>::type* =0)
: ...
{
...
}
...
};
Если U* конвертируется в T*, enable_if
будет иметь член typedef type
по умолчанию аннулировать. То все в порядке. Если U* не конвертируется в T*, этот член typedef отсутствует, подстановка завершается неудачно, а шаблон конструктора игнорируется.
Это решает ваши проблемы конверсии и неоднозначности.
В ответ на комментарий: is_convertible
выглядит примерно так:
typedef char one; // sizeof == 1 per definition
struct two {char c[2];}; // sizeof != 1
template<typename T, typename U>
class is_convertible {
static T source();
static one sink(U);
static two sink(...);
public:
static const bool value = sizeof(sink(source()))==1;
};
Постарайтесь сделать этот конструктор явным, например:
template <typename t>
explicit Pointer(const Pointer<t> &x);
И / или удалить operator T *() const;
- Я думаю, что это также создаст двусмысленность.
РЕДАКТИРОВАТЬ
Проверьте интерфейс std::auto_ptr и сравните с вашим. По крайней мере, они решили эту двусмысленность.