У меня например

#include <iostream>

template <typename T>
struct Base {};

template <>
struct Base<std::string> {
  static const int value = true;
};

template <>
struct Base<std::string &> {
  static const int value = true;
};

int main() {
  bool a = Base<std::string>::value;
  bool b = Base<std::string &>::value;

  std::cout << a << b << std::endl;
}

https://godbolt.org/z/0NpYxB

Заметьте, у меня есть две специализации, которые идентичны, и я хотел бы сократить их до одной. Я знаю два решения, которые я предпочел бы не делать.

(1) Удалите ссылку на сайте вызова, так что требуется только одна специализация.

(2) Создайте базовый класс и унаследуйте от него версии ссылка и нет ссылки .

Есть ли третий вариант, когда специализация является общей для ссылочных и не ссылочных типов?

Требуются решения C ++ 11.

4
bradgonesurfing 2 Май 2019 в 15:43

3 ответа

Лучший ответ

Вы можете выполнить проверку в контексте SFINAE:

// type trait to remove the lvalue-reference
template< class T > struct remove_lvalue_reference      {typedef T type;};
template< class T > struct remove_lvalue_reference<T&>  {typedef T type;};

template <typename T>
using remove_lvalue_reference_t = typename remove_lvalue_reference<T>::type;

template <typename T, typename = void>
struct Base {};

// valid when T is std::string and std::string&
template <typename T>
struct Base<T, typename std::enable_if<std::is_same<std::string, remove_lvalue_reference_t<T>>::value>::type> {
  static const int value = true;
};

ПРЯМОЙ ЭФИР

2
songyuanyao 2 Май 2019 в 13:05

1) Кажется хорошо:

template <typename T>
struct BaseImpl {};

template <>
struct BaseImpl<std::string> {
  static const int value = true;
};

template <typename T>
using Base = BaseImpl<typename std::remove_reference<T>::type>;

2) кажется более многословным

template <typename T>
struct BaseImpl {};

template <>
struct BaseImpl<std::string> {
  static const int value = true;
};

template <typename T>
struct Base : BaseImpl<T> {}; // or directly BaseImpl<std::remove_reference_t<T>>

template <typename T>
struct Base<T&> : BaseImpl<T> {};

3) Аналогично 2), менее многословный, но может быть более сложным

template <typename T>
struct Base : Base<T&> {};

template <typename T>
struct Base<T&> {};

template <>
struct Base : Base<std::string> {
    static const int value = true;
};

1) кажется более читабельным, простым в реализации.

3
Jarod42 2 Май 2019 в 13:09

С enable_if это немного тяжело, но я не думаю, что есть лучший способ.

#include <iostream>
#include <type_traits>

template <typename T, typename Enable = void>
struct Base {};


template <typename T>
struct Base<
    T,
    typename std::enable_if< 
       std::is_same<typename std::decay<T>::type, std::string>::value
    >::type 
>
{
  static const int value = true;
};

int main() {
  bool a = Base<std::string>::value;
  bool b = Base<std::string &>::value;

  std::cout << a << b << std::endl;
}

https://godbolt.org/z/98vzFN

2
bradgonesurfing 2 Май 2019 в 12:54