当特征相同时,如何减少为引用和非引用类型编写特征时的重复

时间:2019-05-02 12:43:53

标签: c++ c++11 templates template-meta-programming

例如,我有

#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解决方案。

3 个答案:

答案 0 :(得分:3)

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)似乎更易读,易于实现。

答案 1 :(得分:2)

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 :(得分:2)

您可以在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;
};

LIVE