Reputation: 36853
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
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
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
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
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