c++templates

C++: `if constexpr` and `std::is_same_v` not working


I'm playing around with templates, compile-time evaluation and pointers to members.

I don't understand why the following code doesn't set a.x to 42 and a.y to 3.14. According to the output, FieldType is unsigned (printed as "j") in the A::x case, and double (printed as "d") in the A::y case. Still, the if constexpr chain doesn't match any of those cases.

#include <iostream>
#include <typeinfo>
#include <type_traits>

struct A {
    unsigned x = 0;
    double y = 0;
};

template<auto Field, typename Class>
void bar(Class* p) {
    using FieldType = decltype(p->*Field);

    if constexpr (std::is_same_v<FieldType, unsigned>) {
        p->*Field = 42;
    }
    else if constexpr (std::is_same_v<FieldType, double>) {
        p->*Field = 3.14;
    }
    else {
        std::cout << "no matching case for field type '" << typeid(FieldType).name() << "'" << std::endl;
    }
}

int main()
{
    A a;
    bar<&A::x>(&a);
    bar<&A::y>(&a);

    std::cout << std::endl;
    std::cout << "a.x: " << a.x << std::endl;
    std::cout << "a.y: " << a.y << std::endl;

    return 0;
}

Playground on Compiler Explorer

Output:

no matching case for field type 'j'
no matching case for field type 'd'

a.x: 0
a.y: 0

Solution

  • Often a simple way to debug type issues in templates is to add static_assert that fails on purpose. Adding static_assert(std::is_same_v<FieldType, unsigned>); gives the following error message:

    <source>: In instantiation of 'void bar(Class*) [with auto Field = &A::x; Class = A]':
    <source>:30:15:   required from here
    <source>:14:24: error: static assertion failed
       14 |     static_assert(std::is_same_v<FieldType, unsigned>);
          |                   ~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    <source>:14:24: note: 'std::is_same_v<unsigned int&, unsigned int>' evaluates to false
    

    So the issue is that FieldType is a reference and that's a different type than plain unsigned.

    std::is_same_v<std::remove_reference_t<FieldType>, unsigned>) or std::is_same_v<FieldType, unsigned&> both fix the problem. See it online.