1

I have just read Is declval<T>() the same as (*(T*)nullptr)? and just realized that I haven't heard of the (*(T*)nullptr) trick before. If I analyze that snippet, it doesn't give me too much information, beside that (quoting the answer)

you can get an instance of T in a decltype without needing to worry about T's constructor

and also the example found there is just very trivial:

struct B {
    A a;
};
typedef decltype((*(B*)nullptr).a) T1;

so, can please someone explain to me what this (*(T*)nullptr) trick is and why (and how) would I want to use it (beside of the example above)?

Edit: decltype(B::a) has the same meaning (https://gcc.godbolt.org/z/Pvjjza) so why overcomplicate it like this? Are there any historical meanings to the appearance of this trick (such as some compiler not supporting it or anything else more useful?)

Ferenc Deak
  • 34,348
  • 17
  • 99
  • 167
  • Does [this thread](https://stackoverflow.com/questions/28532781/how-does-stddeclvalt-work) help? The accepted answers gives some context on why this is done/used. – lubgr Feb 25 '21 at 07:51
  • @lubgr Thanks, I have read that too. I'm just curious if there are any more uses of this weird `(*(T*)nullptr)` construct, or this is the sole purpose it was invented for. – Ferenc Deak Feb 25 '21 at 07:57

1 Answers1

2

nullptr is a null pointer yes!

(B*)nullptr casts the null pointer to a pointer to an object of type B.

*(B*)nullptr dereferences this pointer to a get a reference to an object of type B

(*(B*)nullptr).a accesses the member 'a' of this reference.

Now if you actually executed this in a running program you may crash your program or corrupt your data or maybe nothing will happen for this is undefined behaviour. In any case it is bad and evil and should never be seen in a real program.

However if you enclose the entire expression in decltype you get the return type of the expression rather than executing the expression.

But instead of writing the above ugliness you can use declval. But only use declval inside a decltype

ie

typedef decltype((*(B*)nullptr).a) BMemberA;

generates you a type that is the same as the a member of type B. The alternative and better solution is to write.

typedef decltype(declval<B>().a) BMemberA;
bradgonesurfing
  • 30,949
  • 17
  • 114
  • 217
  • 1
    Yes, but why overcomplicate? Using only: ``decltype(B::a)`` should be enough. https://gcc.godbolt.org/z/Pvjjza – Ferenc Deak Feb 25 '21 at 08:04
  • You asked the question :). I just provided you an explanation as to how they are basically the same thing and that you should use declval. There is no reason not use declval except possibly in older code bases where declval is not available. But then you can use boost::declval – bradgonesurfing Feb 25 '21 at 08:06
  • It works because ``B::a`` is a member access operator **if** you have an instance of ``B`` https://gcc.godbolt.org/z/7oTP1G. It's kind of esoteric and one use can be to access members on base classes that have been shadowed by child classes. – bradgonesurfing Feb 25 '21 at 08:17
  • But I would recommend using declval all the same because even ``decltype(B::a)`` works ``decltype(B)`` doesn't and you can confuse readers of your code. Stick with ``decltype(std::declval())`` and ``decltype(std::declval().a)`` for clarity. – bradgonesurfing Feb 25 '21 at 08:23
  • 1
    Re: "you would get a null pointer exception and crash" -- or you might not be so lucky, and the code would run "just fine". Until, of course, you gave a demo to your most important client, and **that's** when it would crash. – Pete Becker Feb 25 '21 at 14:09
  • You are right. dereferencing a ``nullptr`` is undefined behaviour which means exactly the scenario you described. :) – bradgonesurfing Feb 25 '21 at 14:26
  • Updated the answer to clarify. – bradgonesurfing Feb 25 '21 at 14:30