Detect if T is a reference member at compile time

143 Views Asked by At

I'm trying to detect if an argument is a reference member, at compile time. It does not catch it and I can't figure it why.

#include <type_traits>

struct a {
 a(): i1(42), i2(i1){}
 int  i1;
 int& i2;
};

template <typename T>
void Field(T &field) {

    if constexpr (std::is_pointer_v<T>) {
        //handle pointer
    } else if constexpr (std::is_reference_v<T>) {
        static_assert(!std::is_reference_v<T>, "Reference not supported");
    } else {
        //handle value
    }
}


int main()
{
    a a_;

    Field(a_.i2); // I want this to fail at compile time, but it's not
}

What am I doing wrong?

2

There are 2 best solutions below

2
Jan Schultke On BEST ANSWER

std::is_reference_v works in principle, but not the way you're using it. You have a function template Field(T &field), where the template type parameter T is not going to be deduced to a reference.

a a_;
Field(a_.i2); // calls Field<T>(T&) with [T = int]
              // std::is_reference_v<int> is false

Whatever, you're trying to do here, you could:

  • not work with references directly, but use std::reference_wrapper and always accept a value parameter T field
  • disable deduction by having a std::type_identity_t<T> field parameter, which forces the user to call Field<int&>(a_.i2) manually
    • this could possibly happen through a macro which expands to Field<decltype(a_.i2)>(a_.i2)
0
Pepijn Kramer On

This is closest I can get to what I think you need. I usually don't like to use macros but in this case it could paint me out of a corner. The idea is to use decltype(field) to select the correct template overload explicitly. And to use SFINAE to rule out reference and pointer types. So instead of if constexpr with a static_assert the function will only compile for non-reference and non-pointer types.

Online demo here : https://onlinegdb.com/nkq6bdYD7

#include <iostream>
#include <type_traits>

struct A
{
    A() : i1(42), i2(i1) {}
    int  i1;
    int& i2;
    int* i3;
};

template<typename T>
static constexpr bool is_value()
{
     return !std::is_reference_v<T> && !std::is_pointer_v<T>;
}

template<typename T>
auto Field(const T& value) -> std::enable_if_t<is_value<T>(),void>
{
    std::cout << "Handling field : " << value;
}

#define FIELD(x) Field<decltype(x)>(x);

int main()
{
    A a;

    FIELD(a.i1);
    FIELD(a.i2); // <== will not compile
    FIELD(a.i3); // <== will not compile
    
    return 0;
}