How to detect whether there is a specific PRIVATE member variable in class?

Viewed 301

This question is based on this post.

Goal: I would like to know if a class has the member variable x. I would like to receive true regardless whether or not this variable is private, public or protected.

Approach: You can get the information if a class has a member variable using the following code:


template <typename T, typename = int>
struct HasX : std::false_type { };

template <typename T>
struct HasX <T, decltype((void) T::x, 0)> : std::true_type { };

Use it with

if constexpr (HasX<my_class>::value) {
   // do stuff with x
} else {
   // ...
}

The above code does not work in this case

struct my_class {
private:
   int x;
};

How can I make this work? I would like HasX<my_class>::value to be true.

Ideas:

Use a friend class which has access to T::x. This does not seem to work. Check out this live example.

2 Answers

Well... not sure about correctness and limits of this solution... but...

If you define an helper struct with an x element accessible

struct check_x_helper
 { int x; };

you can write a template struct that inherit from both check_x_helper and the class you want to see if contain a x member

template <typename T>
struct check_x : public T, check_x_helper

Inside check_x you can declare (declare only: are used inside a decltype()) as follows

template <typename U = check_x, typename = decltype(U::x)>
static constexpr std::false_type check (int);

static constexpr std::true_type check (long);

Observe the first one, the template one: when the checked class (T) contains an x member, the decltype(U::x) is ambiguous because x is inherited from both T and check_x_helper, so this function is SFINAE discarded.

On contrary, when T doesn't contains an x member, there isn't an ambiguity, the decltype(U::x) is the type of check_x_helper::x (int) and the first check() function remain enabled.

Now you need something as

using type = decltype(check(0));

static constexpr auto value = type::value;

to call check(0) (the int parameter express the preference to the template version) and save the detected value in a static constexpr variable.

The following is a full compiling example

#include <iostream>
#include <utility>

class foo
 { int x; };

struct bar
 { };

struct check_x_helper
 { int x; };

template <typename T>
struct check_x : public T, check_x_helper
 {
   template <typename U = check_x, typename = decltype(U::x)>
   static constexpr std::false_type check (int);

   static constexpr std::true_type check (long);

   using type = decltype(check(0));

   static constexpr auto value = type::value;
 };

int main()
 {
   std::cout << check_x<foo>::value << std::endl;
   std::cout << check_x<bar>::value << std::endl;
 }

Drawback of this solution: decltype(U::x) fail (ambiguity) also when T declare x as a method or as a using type. So given

class foo
 { int x () { return 0;} ; };

or

class foo
 { using x = int; };

from check_x<foo>::value you obtain 1.

The following seems to work. Please tell me if it needs improvement. Live example.

class Haser {
public:
template <typename T, typename = int>
static constexpr bool HasX = false;

template <typename T> 
static constexpr bool HasX<T, decltype((void) T::x, 0)> = true;
};

struct A { 
private:    
    int x;
    friend Haser;
};

Haser::HasX<A> is true.

Related