Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to reduce duplication when writing traits for reference and non reference types when the traits are the same

I have for example

#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

Note I have two specializations that are identical and would like to reduce it to one. There are two solutions I know of which I'd prefer not to do.

(1) Remove the reference at the call site so that only one specialization is required.

(2) Create a base class and inherit the reference and no reference versions from that.

Is there a third option where the specialization is generic over reference and non reference types?

C++11 solutions are required.

like image 346
bradgonesurfing Avatar asked Mar 04 '23 01:03

bradgonesurfing


2 Answers

1) Seems fine:

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) Seems more verbose

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) Similar to 2), less verbose, but might be more tricky

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) seems the more readable, simple to implement.

like image 88
Jarod42 Avatar answered Apr 07 '23 02:04

Jarod42


You can perform the check in SFINAE context:

// 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

like image 26
songyuanyao Avatar answered Apr 07 '23 01:04

songyuanyao