Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using auto as template parameter

I have a code snippet that works using GCC but not MSVC. It's a simplified block generated with Boost Fusion. Is there anything wrong with the code?

using namespace std;

template<typename T, size_t N>
struct struct_member_mptr;

template<auto mptr>
void callback() {
    cout << "No callback for " << typeid(mptr).name() << endl;
}

template<typename T>
void func(T &u) {
    callback<struct_member_mptr<T, 0>::value>();
    callback<struct_member_mptr<T, 1>::value>();
}

struct Data {
    int a;
    char b;
};

template<>
struct struct_member_mptr<Data , 0> {
    typedef decltype(Data::a) Data::*type;
    static constexpr type value = &Data::a ;
};

template<>
struct struct_member_mptr<Data , 1> {
    typedef decltype(Data::b) Data::*type;
    static constexpr type value = &Data::b ;
};

template<>
void callback<&Data::a>() {
    cout << "postParse D::a" << endl;
}

template<>
void callback<&Data::b>() {
    cout << "postParse D::b" << endl;
}

int main() {
    Data i;
    func(i);
}

The expected result is:

postParse D::a
postParse D::b

and I have it with GCC

But the result from MSVC is:

No callback for int Data::* __ptr64
No callback for char Data::* __ptr64

UPD From Vincent Saulue-Laborde: reduced case, the issue is reproducible at compile time

template<auto mptr>
constexpr int callback() {
    return 0;
}

template<>
constexpr int callback<2>() {
    return 5;
}

constexpr int v2 = 2;

static_assert(callback<v2>() == 5);
like image 748
Anton Avatar asked Dec 20 '25 03:12

Anton


1 Answers

As Vincent Saulue-Laborde pointed out in the comments, the problem is that MSVC treats struct_member_mptr<Data, 0>::value as having type int Data::* const (with const qualification), while your specialization callback<&Data::a> expects a parameter of type int Data::* (without const). MSVC fails to match these as the same specialization.

Some workarounds that should work with both GCC and MSVC:

Option 1: Explicit specialization with the full path

template<>
void callback<struct_member_mptr<Data, 0>::value>() {
    cout << "postParse D::a" << endl;
}

template<>
void callback<struct_member_mptr<Data, 1>::value>() {
    cout << "postParse D::b" << endl;
}

Option 2: Use a wrapper to remove const qualification

template<auto mptr>
void callback_impl() {
    cout << "No callback for " << typeid(mptr).name() << endl;
}

template<auto mptr>
void callback() {
    callback_impl<mptr>();  // This forces a new instantiation without const
}

Option 3: Use typed template parameters instead of auto

template<typename T, T mptr>
void callback() {
    cout << "No callback for " << typeid(mptr).name() << endl;
}

// Then in func:
template<typename T>
void func(T &u) {
    using mptr_type_0 = typename struct_member_mptr<T, 0>::type;
    using mptr_type_1 = typename struct_member_mptr<T, 1>::type;
    callback<mptr_type_0, struct_member_mptr<T, 0>::value>();
    callback<mptr_type_1, struct_member_mptr<T, 1>::value>();
}

Option 4: Force value category with a helper

template<auto V>
static constexpr auto nonconst_value = V;

template<typename T>
void func(T &u) {
    callback<nonconst_value<struct_member_mptr<T, 0>::value>>();
    callback<nonconst_value<struct_member_mptr<T, 1>::value>>();
}
like image 120
kingkongda3rd Avatar answered Dec 21 '25 18:12

kingkongda3rd



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!