2
class Foo {
public:
    static const int BAR = 2;
};

typedef Foo T1;
typedef Foo* T2;

int value1 = T1::BAR;  // This works.
int value2 = T2::BAR;  // This doesn't work.

Can the value of BAR be extracted from T2?

I am using c++-11.

I imagine this would be bad practice but am curious if it can be done.

cigien
  • 57,834
  • 11
  • 73
  • 112
  • Did you solve your brand new question that you just deleted? I was typing an answer when your question went away... If you find the solution yourself, write your own answer. Don't delete a question just because you no longer need it... Q&A on this site is for future readers. – Ben Voigt Feb 08 '23 at 20:31

3 Answers3

6

Yes.

You can strip the pointer using traits:

int value2 = std::remove_pointer<T2>::type::BAR;

Or alternatively (since C++14):

int value2 = std::remove_pointer_t<T2>::BAR;

(referencelive demo)

It's not even bad practice, unless there's some more expressive way to achieve your real goal.

Asteroids With Wings
  • 17,071
  • 2
  • 21
  • 35
1

You could create an object of type T2, and access the member BAR, like this:

int value2 = T2{}->BAR;

Note that it's debatable whether dereferencing a null pointer to access a static data member is well-formed or not. Whether this solution is correct or not, I'd suggest using Asteroids With Wings' answer as it is much clearer in expressing the intent of what the code is supposed to achieve.

cigien
  • 57,834
  • 11
  • 73
  • 112
  • 1
    That is potentially very inefficient, and even outright wrong. – Blindy Dec 04 '20 at 17:06
  • @Blindy Oh, why is it incorrect? Seems all right to me. – cigien Dec 04 '20 at 17:07
  • Imagine instantiating a class that creates a bunch of OpenGL vertex array objects, shader programs and whatever else it needs, just to access a constant from it, and then destroy it. You don't even know if the thread you're on has a GL context bound. I'm sure it doesn't seem wrong to you, but that doesn't make it true. – Blindy Dec 04 '20 at 17:09
  • Whether or not `T2{}->BAR` has well-defined behaviour is controversial; there is no instance there. You may not be accessing any non-static members in practice, but at the language layer you're still dereferencing a null pointer. On pages like https://stackoverflow.com/questions/28482809/c-access-static-members-using-null-pointer you'll find conflicting conclusions, which IMO is enough reason to avoid it. – Asteroids With Wings Dec 04 '20 at 17:09
  • It's also just logically the wrong tool for the job, because the semantics of the task do not involve creating any object – Asteroids With Wings Dec 04 '20 at 17:10
  • 1
    @Blindy Sure, but what does that have to do with `Foo`? – cigien Dec 04 '20 at 17:10
  • @AsteroidsWithWings If I'm reading that link correctly, that deals with dereferencing a null pointer. `T{}` creates a valid instance doesn't it? – cigien Dec 04 '20 at 17:13
  • 1
    If your code is only designed to work for an empty class, you should say so and I won't have an issue with it. However as a general answer, this is both inefficient and semantically wrong, as I said before. – Blindy Dec 04 '20 at 17:13
  • @cigien A valid default-constructed instance of `T2`, yes, and since `T2` is a pointer type, that's a null pointer. – Asteroids With Wings Dec 04 '20 at 17:14
  • 1
    @Blindy As is the case with answers in general, the code is designed to work with exactly the class the OP provided, no more no less. I'm not sure I want to add the caveats that you're suggesting to the answer. – cigien Dec 04 '20 at 17:14
  • @AsteroidsWithWings Oh, I see, of course, that was silly. I'll read through the link more thoroughly, it does seem to indicate that this is well formed. – cigien Dec 04 '20 at 17:15
  • @cigien Again, some people say it is while others say it isn't. It's a famously controversial talking point. Best to avoid it, by using _clearly_ well-defined code with the appropriate semantics for your task. – Asteroids With Wings Dec 04 '20 at 17:19
  • @AsteroidsWithWings Sure, I understand. I'm not yet convinced either way yet, I'll have to look into it more. But since I'm not yet sure the answer is wrong, I'll leave it up. I'll delete it for sure if I end up thinking it's wrong. – cigien Dec 04 '20 at 17:20
  • Well it's definitely the wrong answer, whether the line is safe or not ;) – Asteroids With Wings Dec 04 '20 at 17:23
  • @AsteroidsWithWings Yes, I agree with the part about preferring the option that is clearly well-defined. I've edited accordingly. – cigien Dec 04 '20 at 17:24
  • 1
    @AsteroidsWithWings Hmm, "wrong" might be strong. You mean "poorer", or "inappropriate", right? That's reasonable. – cigien Dec 04 '20 at 17:25
  • @Blindy How is this going to "create a bunch of OpenGL vertex array objects"? – Asteroids With Wings Dec 04 '20 at 17:26
  • It could be worse: `int value2 = remove_reference::type::BAR;` At least that is safe and well-defined when `T2` is a pointer type, albeit it is not as efficient as using `std::remove_pointer(_t)` ([demo](https://ideone.com/kFsT6X)) – Remy Lebeau Dec 04 '20 at 17:51
1

Lets look at the error first (you should always do that):

<source>:11:14: error: 'T2' (aka 'Foo *') is not a class, namespace, or enumeration
int value2 = T2::BAR;// This doesn't work.

It is quite clear. Foo* is indeed not a class type, namepsace or enumeration.

You can remove the pointer from a type via std::remove_pointer:

int value2 = std::remove_pointer_t<T2>::BAR; // This is awkward, but no error
463035818_is_not_an_ai
  • 109,796
  • 11
  • 89
  • 185