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?

Boann
  • 48,794
  • 16
  • 117
  • 146
Kirill Dmitrenko
  • 3,474
  • 23
  • 31
  • I can't even parse that `__countof_helper` declaration... – Max Langhof Dec 18 '19 at 14:51
  • 1
    @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 `char`s as in the argument array. Thus `sizeof` of that `char` array should be the same as number of elements in the argument array. – Kirill Dmitrenko Dec 18 '19 at 14:54
  • @KirillDmitrenko Why does it do all this? *That* seems more interesting than `+0`. Couldn't `__countof_helper` just return `_SizeOfArray`? – François Andrieux Dec 18 '19 at 15:09
  • 1
    @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](https://cs.chromium.org/chromium/src/v8/src/base/macros.h?type=cs&q=ArraySizeHelper&sq=package:chromium&g=0&l=27-30) similar impl in Chromium and [here's](https://www.viva64.com/en/a/0074/) an article which contains full explanation why something like this is preferable and less error-prone (dare I even say, error free). – Kirill Dmitrenko Dec 18 '19 at 15:19
  • 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. – François Andrieux Dec 18 '19 at 15:47

1 Answers1

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;
}
Adrian Mole
  • 49,934
  • 160
  • 51
  • 83
  • Can you share an example of a context where this happens? – François Andrieux Dec 18 '19 at 15:34
  • 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`. – François Andrieux Dec 18 '19 at 15:52
  • @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? – Adrian Mole Dec 18 '19 at 15:56
  • 1
    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. – François Andrieux Dec 18 '19 at 15:59
  • @FrançoisAndrieux One could *maybe* 'break' the added parentheses by trying to define a cast/converter operation with: `#define BESILLY(arg) int _countof(arg)` – Adrian Mole Dec 18 '19 at 16:14
  • 3
    `sizeof` is not `size_t`. It's not a type. I don't see how it could ever trigger the MVP. – T.C. Dec 18 '19 at 19:18
  • @T.C. 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. – Adrian Mole Dec 18 '19 at 23:17
  • Can you cite any source that this is the reason for it? The guy who actually wrote the code says differently. – M.M Dec 18 '19 at 23:53
  • @M.M I can only refer the caller to my comment, above, or maybe to this alternative: https://stackoverflow.com/a/9679201/10871073 – Adrian Mole Dec 19 '19 at 01:20