Erel Segal-Halevi
Erel Segal-Halevi

Reputation: 36853

How to check if two types are the same, ignoring const and reference?

In C++, it is possible to use std::is_same to check if two types are exactly identical. Is there a way to check if two types are identical except, maybe, a const or a & modifier? Here is an example:

#include <type_traits>
#include <iostream>
using namespace std;

int main() {
    cout << boolalpha;
    cout << is_same<char,int>::value << endl;    // false - OK
    cout << is_same<char,char>::value << endl;   // true  - OK
    cout << is_same<char,const char>::value << endl;  // false - should be true
    cout << is_same<char,const char&>::value << endl; // false - should be true
}

Upvotes: 9

Views: 3301

Answers (4)

Quuxplusone
Quuxplusone

Reputation: 27370

To strip only top-level cvref-qualifiers, the answer is simply

template<class T, class U>
inline constexpr bool is_same_uncvref_v = std::is_same_v<
    std::remove_cvref_t<T>,
    std::remove_cvref_t<U>
>;

or, pre-C++20,

template<class T, class U>
inline constexpr bool is_same_uncvref_v = std::is_same_v<
    std::remove_cv_t<std::remove_reference_t<T>>,
    std::remove_cv_t<std::remove_reference_t<U>>
>;

As Erel (almost) pointed out above, this is equivalent to

template<class T, class U>
inline constexpr bool is_same_uncvref_v = std::is_same_v<
    const volatile std::remove_reference_t<T>,
    const volatile std::remove_reference_t<U>
>;

Still, this fails to detect the similarity between int* and const int*, or between const int** and int *const *. For that kind of similarity, we need [conv.qual]/2's notion of "similar type." See Implementing an `is_similar` type trait based on the definition of the C++ standard . I implement that trait in my libc++ fork as follows:

template <class _Tp, class _Up>
struct is_similar : conditional<
        is_const<_Tp>::value || is_volatile<_Tp>::value ||
            is_const<_Up>::value || is_volatile<_Up>::value,
        is_similar<typename remove_cv<_Tp>::type, typename remove_cv<_Up>::type>,
        is_same<_Tp, _Up>
    >::type {};

template <class _Tp, class _Up>
struct is_similar<_Tp*, _Up*>: is_similar<_Tp, _Up> {};

template <class _Tp, class _Up, class _Cp>
struct is_similar<_Tp _Cp::*, _Up _Cp::*>: is_similar<_Tp, _Up> {};

template <class _Tp, class _Up>
struct is_similar<_Tp[], _Up[]>: is_similar<_Tp, _Up> {};

template <class _Tp, class _Up, size_t _Np>
struct is_similar<_Tp[], _Up[_Np]>: is_similar<_Tp, _Up> {};

template <class _Tp, class _Up, size_t _Np>
struct is_similar<_Tp[_Np], _Up[]>: is_similar<_Tp, _Up> {};

template <class _Tp, class _Up, size_t _Np>
struct is_similar<_Tp[_Np], _Up[_Np]>: is_similar<_Tp, _Up> {};

Upvotes: 1

Kai Petzke
Kai Petzke

Reputation: 2984

In many cases, std::is_same_v<std::decay_t<TYPE_TO_TEST>, TYPE_TO_COMPARE> will do the trick. std::decay_t<TYPE> also converts array types to pointers to the array members, so std::is_same_v<std::decay_t<int[5]>, int*> also yields true. But in many cases, this behavior is even beneficial, especially, if your testing requirement is "can be used like a pointer".

Upvotes: 3

Erel Segal-Halevi
Erel Segal-Halevi

Reputation: 36853

I found an alternative solution: instead of removing const and &, we can add them:

template<class T1, class T2>
bool is_almost_same_v = std::is_same_v<const T1&,const T2&>;

Indeed:

cout << is_almost_same_v<char,int> << endl;    // false
cout << is_almost_same_v<char,char> << endl;   // true
cout << is_almost_same_v<char,const char> << endl;  // true
cout << is_almost_same_v<char,const char&> << endl; // true

Upvotes: 4

Tejendra
Tejendra

Reputation: 1944

Removing cv-qualifiers as well as returning a non reference type will be supported from C++20 onward std::remove_cvref

However as of current standard, you can use Type modifications functions in conjunction

template<class T1, class T2>
void print_is_same() {
  std::cout << std::is_same<T1, T2>() << '\n';
}

int main() {
  std::cout << std::boolalpha;

  print_is_same<char, int>(); //false
  print_is_same<char, char>(); //true

  print_is_same<char, std::remove_const<const char>::type>(); //true
  print_is_same<char, std::remove_const<std::remove_reference<const char &>::type>::type>(); //true
}

Or probably create a type alias such as

template<typename T>
using base_type = typename std::remove_cv<typename std::remove_reference<T>::type>::type;

Upvotes: 8

Related Questions