#c #templates #operators #typetraits
#c #шаблоны #операторы #признаки типов
Вопрос:
template<typename T>
struct foo
{
T* p;
foo(T* x) : p(x) {}
~foo() { if(p) delete p; }
Tamp; operator*() const { return *p; }
};
int main()
{
foo<int> i(new int);
foo<void> v(new int); // <= illegal use of type 'void'
}
Если T = void, то я не хочу реализовывать operator *(). Как я могу этого добиться? Я не хочу специализировать класс, потому что в моем классе есть много других методов.
PS: Пожалуйста, обратите внимание, что это всего лишь пример, объясняющий мою проблему.
Ответ №1:
Вы можете переместить все другие методы (которые хорошо сочетаются с T==void
) в базовый класс и сделать foo
производными от него. Тогда foo
можно специализироваться, чтобы не объявлять operator*
для T==void
template <typename T>
struct foobase {
T* p;
foobase(T* x) : p(x) {}
~foobase() { if(p) delete p; }
};
template <typename T>
struct foo : foobase<T> {
Tamp; operator*() const { return *p; }
};
template<>
struct foo<void> : foobase<void> {
};
Ответ №2:
Стандарт C 11 решил эту проблему для std::unique_ptr
примерно так:
typename std::add_lvalue_reference<T>::type
operator*() const { return *p; }
Комментарии:
1. Хм, я мог бы использовать это и в C : имя типа std::add_reference<T>::оператор типа* () const { return *p; }
Ответ №3:
Вот так:
template<typename T>
struct foo_base
{
T* p;
foo(T* x) : p(x) {}
~foo() { if(p) delete p; }
// other methods …
};
template<typename T>
struct foo : foo_base<T>
{
Tamp; operator*() const { return *p; }
};
template<>
struct foo<void> : foo_base<void>
{
};
Комментарии:
1. ( 1) Интересно, что, когда я добавлял пример кода к своему первоначальному ответу, кажется, что мы немного пересеклись, за исключением того, что я выбрал
foobase
вместоfoo_base
. Ну что ж .. 🙂2. @Alexander Я не дал дополнительных объяснений: ( но мое форматирование кода лучше 🙂
Ответ №4:
Как насчет
typename disable_if<is_void<T>, T>::typeamp; operator* () const { return *p;}
или я упускаю здесь что-то очевидное?
Комментарии:
1.
disable_if
но в остальном я согласен.2. Это не работает, потому что когда ‘T = void’, то ‘T amp;’ недопустимо: ‘абстрактный декларатор’ : незаконное использование типа ‘void’
3. SFINAE работает только для шаблонных функций, то есть он не работает для не шаблонных функций-членов шаблонных классов, как в этом случае. Вы также могли бы создать шаблон функции-члена и проверить, совпадает ли тип с
T
, но это было бы не совсем элегантно.
Ответ №5:
Что в этом решении:
template<typename T>
struct foo
{
T* p;
foo(T* x) : p(x) {}
~foo() { if(p) delete p; }
template<typename U> struct impl { Uamp; deref(U* p) { return *p; } };
template<> struct impl<void> { void deref(void* p) { } };
typename boost::conditional<std::is_void<T>::value, T, typename std::add_reference<T>::type>::type operator*() const
{
static_assert(!std::is_void<T>::value, "illegal use of type 'void'");
return impl<T>().deref(p);
}
};