Специализация функции-члена класса шаблона по типу

Я пытаюсь специализировать функцию-член в шаблонном классе по признаку типа его параметра шаблона, но мое предварительное объявление явно неверно. Есть ли легкое решение?

#include <type_traits>

template <typename T>
class TTest{
public:
  T data;

  // edited to comment this out, template<typename U>
  bool operator !=(const TTest& other) const;
};

template <typename T>
typename std::enable_if<std::is_unsigned<T>::value, bool>::type
TTest<T>::operator !=(const TTest<T>& other) const{
  return true;
}

template<typename T>
bool TTest<T>::operator !=(const TTest<T>& other) const{
  return false;
}

int main(){
  TTest<size_t> t1;
  TTest<int> t2;
}

Кланг говорит мне:

templateTest.cpp:13:11: error: out-of-line definition of 'TTest::operator!='
  differs from the declaration in the return type
TTest<T>::operator !=(const TTest<T>& other) const{
          ^
templateTest.cpp:8:8: note: previous declaration is here
  bool operator !=(const TTest& other) const;
       ^
1 error generated.

person Ben Jones    schedule 08.01.2014    source источник
comment
Этот код не имеет смысла. Шаблон члена не зависит от параметра шаблона, поэтому нечего специализировать.   -  person Kerrek SB    schedule 09.01.2014
comment
Вы понимаете, что const TTest& other означает const TTest<T>& other в вашем шаблоне rmember?   -  person Kerrek SB    schedule 09.01.2014
comment
Вы не можете частично специализировать шаблон функции. Если вам нужна версия для беззнакового T и беззнакового T, то вам нужно либо перегрузить +SFINAE, либо частично специализировать весь шаблон класса.   -  person dyp    schedule 09.01.2014
comment
@Kerrek Я упростил код. В зависимости от того, является ли T знаковым или беззнаковым, реализация оператора != будет разной. Редактировать: Упс, бит имени типа U остался после одной попытки исправить его. Я отредактирую вопрос   -  person Ben Jones    schedule 09.01.2014


Ответы (3)


Кажется, что весь enable_if шебанг является частью сигнатуры функции (или я не очень понимаю ошибки). Я могу заставить код компилироваться и вести себя так, как вы хотите, если я изменю его на

template <typename T>
class TTest{
public:
  T data;

  template<typename U>
  typename std::enable_if<std::is_unsigned<U>::value, bool>::type
  operator !=(const TTest<U>& other) const;

  template<typename U>
  typename std::enable_if<not std::is_unsigned<U>::value, bool>::type
  operator !=(const TTest<U>& other) const;
};

template <typename T>
template <typename U>
typename std::enable_if<std::is_unsigned<U>::value, bool>::type
TTest<T>::operator !=(const TTest<U>&) const{
  return true;
}

template <typename T>
template <typename U>
typename std::enable_if<not std::is_unsigned<U>::value, bool>::type
TTest<T>::operator !=(const TTest<U>&) const{
  return false;
}

Текущая демонстрация. Конечно, это становится намного менее повторяющимся, если вы определяете эти операторы встроенными.

Лучшим подходом может быть отправка в разные частные реализации логики оператора на основе свойств T. Это удаляет всю многословность SFINAE из вашего кода.

template <typename T>
class TTest{
public:
  T data;

  bool operator!=(const TTest& other) const
  {
      return not_equal_to(other, typename std::is_unsigned<T>::type());
  }

private:
  bool not_equal_to(TTest const&, std::true_type) const
  {
      return true;
  }

  bool not_equal_to(TTest const&, std::false_type) const
  {
      return false;
  }
};

Демо

person Praetorian    schedule 08.01.2014
comment
Первый ответ, кажется, работает, но у меня есть 2 вопроса: если я встраиваю эти определения, я получаю двусмысленность/не могу вывести ошибки (в зависимости от того, использую ли я ‹typename U = T› или ‹typename U›. Любая идея, почему? Второй , я не очень хочу сравнивать TTests разных типов, но этот подход, похоже, позволяет это сделать. - person Ben Jones; 09.01.2014
comment
Я не получаю ошибок с gcc или clang со встроенными определениями. Вы не можете избавиться от template<typename U>, потому что для работы SFINAE требуется вывод аргумента шаблона. Чтобы убедиться, что вы не сравниваете разные типы, вы можете добавить static_assert(std::is_same<T,U>::value, "") в определения. В любом случае, я рекомендую вам вообще не использовать enable_if и следовать второму подходу, если только вам что-то не мешает это сделать. - person Praetorian; 09.01.2014
comment
Ой, я пропустил пару фрагментов, вставив это сюда, работает и на моей машине, спасибо. Почему вы предпочитаете второй подход? Меньше шума шаблона? - person Ben Jones; 09.01.2014
comment
@BenJones Да, меньше подробностей и нет необходимости в template<typename U> - person Praetorian; 09.01.2014
comment
Я предполагаю, что после того, как все будет встроено, за этот дополнительный параметр не будет взиматься плата? - person Ben Jones; 09.01.2014
comment
@BenJones Конечно, нет никакой гарантии, но я был бы очень удивлен, если бы оптимизатор решил не избавляться от этого. - person Praetorian; 09.01.2014

Вы объявили шаблон функции-члена вашего шаблона класса и пытаетесь специализировать его как функцию-член. Это не взлетит. Кроме того, типы возвращаемых значений различаются, хотя в конечном итоге они оцениваются как одно и то же.

Я не знаю, что вы пытаетесь сделать, поскольку тип U шаблона функции-члена даже не выводится (вы имели в виду, что аргумент должен иметь тип TTest<U>?). Если вы хотите специализировать свой член на основе признака, я думаю, вам нужно либо перегрузить оператор, либо использовать другой подход (например, делегирование реализации специализированному шаблону класса):

#include <iostream>
#include <type_traits>

template <typename T>
class TTest{
public:
  T data;

  template<typename U>
  typename std::enable_if<!std::is_unsigned<U>::value, bool>::type
  operator !=(const TTest<U>& other) const;

  template<typename U>
  typename std::enable_if<std::is_unsigned<U>::value, bool>::type
  operator !=(const TTest<U>& other) const;
};

template <typename T>
template<typename U>
typename std::enable_if<!std::is_unsigned<U>::value, bool>::type
TTest<T>::operator !=(const TTest<U>& other) const {
    return true;
}

template <typename T>
template<typename U>
typename std::enable_if<std::is_unsigned<U>::value, bool>::type
TTest<T>::operator !=(const TTest<U>& other) const {
    return false;
}

int main(){
  TTest<unsigned int> t1;
  TTest<int> t2;
  std::cout << std::boolalpha
            << "t1 != t1: " << (t1 != t1) << '\n'
            << "t1 != t2: " << (t1 != t2) << '\n'
            << "t2 != t1: " << (t2 != t1) << '\n'
            << "t2 != t2: " << (t2 != t2) << '\n';
}
person Dietmar Kühl    schedule 08.01.2014
comment
В вашем объявлении класса вместо этого он сделал бы template‹typename U = T›. Тогда это имеет смысл (и будет работать). Читаемость на другом конце.... - person jyavenard; 03.05.2015

Диспетчеризация тегов — это чистый способ сделать это:

template <typename T>
class TTest{
  bool not_equal( const ITest& other, std::true_type /* is unsigned */ ) const;
  bool not_equal( const ITest& other, std::false_type /* is unsigned */ ) const;
public:
  T data;

  bool operator !=(const TTest& other) const {
    return not_equal( other, std::is_unsigned<T>() );
  }
};

теперь просто реализуйте две перегрузки TTest<T>::not_equal. Только тот, который действительно вызывается для данного T, будет скомпилирован после базового синтаксического анализа.

person Yakk - Adam Nevraumont    schedule 08.01.2014