C enable_if (или обходной путь) для оператора-члена

#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); 
    }
};