4

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.

463035818_is_not_an_ai
  • 109,796
  • 11
  • 89
  • 185
User12547645
  • 6,955
  • 3
  • 38
  • 69
  • If the member is private, then it cannot be used from outside the class, so how would the code with the `if contexpr` take advantage of knowing that info? – Enlico Sep 30 '20 at 14:20
  • I would like to use `my_class::x` from a class which is `friend` of `my_class`. – User12547645 Sep 30 '20 at 14:22
  • 3
    @Enrico actually, any private/protected member can be accessed via [template specialization](http://bloglitb.blogspot.com/2010/07/access-to-private-members-thats-easy.html). Though I don't think it's possible to _test for the existence_ of something inaccessible – Human-Compiler Sep 30 '20 at 14:22
  • If you can modify the class to add a friend you can as well inspect the definition to see if the member exists ;) – 463035818_is_not_an_ai Sep 30 '20 at 14:22
  • @idclev463035818 I get what you mean. Let's say this member function is templated and I receive an object of type `T`. Then I do not know if `T::x` exists. – User12547645 Sep 30 '20 at 14:25
  • @User12547645, so is the `if constexpr` inside `my_class`'s `friend` class? – Enlico Sep 30 '20 at 14:33
  • there is a trick, if `T` has a method template, this is a backdoor to access whatever privates of `T`. There are other ways to access privates, but beware. Many "solutions" you find (even here on SO) are not valid C++, they make use of some UB and are mainly based on luck – 463035818_is_not_an_ai Sep 30 '20 at 14:36
  • though to just check existance you need no access (i believe). But wait, if `T` made your class (the one that wants to acces `x`) a friend then your `HasX` can work with a modifcation, the acces has to happen in the class that is declared as friend. – 463035818_is_not_an_ai Sep 30 '20 at 14:39
  • I don't quite understand @idclev463035818. Could you give an example? – User12547645 Sep 30 '20 at 14:39
  • will take some time before I can give it a try, but i got hooked up, will come here later – 463035818_is_not_an_ai Sep 30 '20 at 14:44
  • Thank you very much @idclev463035818. Also please have a look at my solution. Just in case it needs improvement ;) – User12547645 Sep 30 '20 at 14:44
  • my ideas were all variations of either of the two answers you already got. If the aim is to have a template that is declared as friend of `A`, ie it can access `A`s private `x` but needs to know whether that exists, then perhaps making the trait a friend of `A` is the simplest. Actually thats your solution (and one of the answers) – 463035818_is_not_an_ai Sep 30 '20 at 18:07

2 Answers2

5

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.

max66
  • 65,235
  • 10
  • 71
  • 111
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.

User12547645
  • 6,955
  • 3
  • 38
  • 69
  • 1
    Yeah, but what's the point? You've made the `Haser` class a `friend` of `A`, and it obviously can see the private members now. But this is possible just because you have the source code of `A`, otherwise you couldn't do that. It'd be good if you could explain, in the question, why you need this. Maybe someone else would learn something too (beside the mostly obvious thing that `friend`s can see `private`s). – Enlico Sep 30 '20 at 15:00