Home > database >  Using std::is_same with structural (non-type) template parameters
Using std::is_same with structural (non-type) template parameters

Time:07-15

Consider a templated type containing a structural template parameter of any type. For the purpose of the example value_type<auto V> is defined.

We also declare a constexpr structure containing some member integral types with custom constructors that set the member values using a non-trivial expression (requiring more than memory copy).

Try feeding the structure into value_type and comparing it against another instance using std::is_same.

Example code to illustrate the case:

#include <iostream>
#include <type_traits>

template <auto V>
struct value_type
{
    using type = decltype(V);
    static constexpr type value = V;
};

struct hmm
{
    //Both constructors set b=4 by default
    constexpr hmm(int x, int y = 8) : a(x), b(y / 2) { }
    constexpr hmm(float c, int z = 2) : a((int)c), b(z * 2) { }

    const int a;
    const int b;
    friend constexpr bool operator==(const hmm& a, const hmm& b) { return false; }
    friend constexpr bool operator!=(const hmm& a, const hmm& b) { return true; }
};

int main()
{
    std::cout << (std::is_same_v<value_type<hmm(2)>, value_type<hmm(3.5f)>>) << ", ";
    std::cout << (std::is_same_v<value_type<hmm(5)>, value_type<hmm(5.11112f)>>) <<  ", ";
    std::cout << (std::is_same_v<value_type<hmm(5, 7)>, value_type<hmm(5.11112f)>>) <<  ", ";
    std::cout << (std::is_same_v<value_type<hmm(5, 12)>, value_type<hmm(5.11112f, 3)>>) <<  std::endl;
    return 0;
}

This code prints 0, 1, 0, 1 on gcc, msvc, and clang. It makes perfect sense, however, it got me wondering what are the limits of this mechanism.

  1. How exactly is the comparison of those types performed?
  2. Is this behavior standardized across compilers or is it just pure luck that they all seem to follow the same pattern here?

From what it looks like, their members are checked after construction, however apparently without using the comparison operators.

  1. Is there a standard compliant way to override this comparison?

And, more generally, according to a potential is_same implementation using <T,T> specialization (source):

template<class T, class U>
struct is_same : std::false_type {};
 
template<class T>
struct is_same<T, T> : std::true_type {};
  1. What are the rules for matching types T, U to be considered the same entity in the context of <T,T> specialization?

CodePudding user response:

  1. How exactly is the comparison of those types performed?
  2. Is this behavior standardized across compilers or is it just pure luck that they all seem to follow the same pattern here?

As per NTTP on cppref, emphasis mine:

An identifier that names a non-type template parameter of class type T denotes a static storage duration object of type const T, called a template parameter object, whose value is that of the corresponding template argument after it has been converted to the type of the template parameter. All such template parameters in the program of the same type with the same value denote the same template parameter object. A template parameter object shall have constant destruction.

And type equivalence:

Template argument equivalence is used to determine whether two template-ids are same.

Two values are template-argument-equivalent if they are of the same type and

  • they are of integral or enumeration type and their values are the same
  • or they are of pointer type and they have the same pointer value
  • or they are of pointer-to-member type and they refer to the same class member or are both the null member pointer value
  • or they are of lvalue reference type and they refer to the same object -> - or function
  • or they are of type std::nullptr_t
  • or they are of floating-point type and their values are identical
  • or they are of array type (in which case the arrays must be member objects of some class/union) and their corresponding elements are template-argument-equivalent
  • or they are of union type and either they both have no active member or they have the same active member and their active members are template-argument-equivalent
  • or they are of floating-point type and their values are identical
  • or they are of non-union class type and their corresponding direct subobjects and reference members are template-argument-equivalent

There's no comparison but only type equivalence involved. If they have the same value, they all refer to the same object. That's it.

In your case, hmm(5) and hmm(5.11112f) refers to the same object, but not between hmm(2) and hmm(3).

  1. Is there a standard compliant way to override this comparison?

I believe it's not allowed now.

  1. What are the rules for matching types T, U to be considered the same entity in the context of <T,T> specialization?

They have the exact same type with cv-quailifiers.

  • Related