Why does the MSVC _count_of implementation add 0 to the result of sizeof? [duplicate]
Asked Answered
B

1

6

I've been reading the implementation of the _countof macro in MSVC and found a detail I can't explain. It's implemented via a __crt_countof macro which on C++ is expanded to (sizeof(*__countof_helper(_Array)) + 0) (here's the relevant code from the header). Why is + 0 there? What would go amiss without it?

Bonnell answered 18/12, 2019 at 14:36 Comment(5)
I can't even parse that __countof_helper declaration...Callaway
@MaxLanghof Took me some time to parse it too:) It declares a function template that returns a pointer to a fixed-size array of the same number of chars as in the argument array. Thus sizeof of that char array should be the same as number of elements in the argument array.Bonnell
@KirillDmitrenko Why does it do all this? That seems more interesting than +0. Couldn't __countof_helper just return _SizeOfArray?Saville
@FrançoisAndrieux It's safer than traditional sizeof(a) / sizeof(a[0]). If you accidentally pass a pointer instead of a static array to that construct, you'll get bad result. Linked implementation won't compile for a pointer. Here's similar impl in Chromium and here's an article which contains full explanation why something like this is preferable and less error-prone (dare I even say, error free).Bonnell
I guess this code predates constexpr. And the code I linked compiles but it wouldn't actually run. I see now why it does it this way. It communicates the size through type information which keeps it a compile time constant.Saville
J
6

The + 0 is added to prevent a potential occurrence of the Most Vexing Parse! Without it, an expression like sizeof(*__countof_helper(_Array)) could be taken as a function declaration in some circumstances.

EDIT: I'm currently trying to work up an example context (as per request in the comment). In the meantime, this much-simplified 'equivalent' (something I have actually encountered) may be helpful:

#include <iostream>
#include <vector>

int main() {
    int num = 2;
//  std::vector<char> vec(size_t(num));     // Won't compile - Most Vexing Parse
    std::vector<char> vec(size_t(num) + 0); // Compiles - no longer a func decl!
    vec[0] = 'a';
    vec[1] = 'b';
    std::cout << vec[0] << ' ' << vec[1] << std::endl;
    return 0;
}
Jeaninejeanlouis answered 18/12, 2019 at 15:19 Comment(9)
Can you share an example of a context where this happens?Saville
Extra parenthesis around size_t(num) also fixes it, which the macro has. So it looks to me like the macro already defends against that without +0.Saville
@FrançoisAndrieux But the M/S coder who implemented that particular header may not have been sure, so added the +0 just to be on the safe side?Jeaninejeanlouis
Definitely a possibility. The intention may have been to prevent mvp. In any case it does no harm and it makes sense that nobody wants to take on the responsibility of removing it and risking breaking someone's code by accident if it turns out it does do something unexpected.Saville
@FrançoisAndrieux One could maybe 'break' the added parentheses by trying to define a cast/converter operation with: #define BESILLY(arg) int _countof(arg)Jeaninejeanlouis
sizeof is not size_t. It's not a type. I don't see how it could ever trigger the MVP.Gaultheria
@Gaultheria Exactly the point. My efforts to devise an example have so far failed! I can make it happen with size_t but not with sizeof! Here, I can only suggest there may have been a time (still could be, somewhen?) when sizeof was implemented function-style. And let's not forget that it's best not to "fix" other folks' code if you don't know why you're 'fixing' it.Jeaninejeanlouis
Can you cite any source that this is the reason for it? The guy who actually wrote the code says differently.Agneta
@Agneta I can only refer the caller to my comment, above, or maybe to this alternative: https://mcmap.net/q/1147741/-what-39-s-the-purpose-of-dummy-addition-in-this-quot-number-of-elements-quot-macroJeaninejeanlouis

© 2022 - 2024 — McMap. All rights reserved.