0

Using GCC, it seems template argument substitution always fails with a zero-sized array. I would expect the static_assert to fail and print the message in test1, just like it does with test2.
You can also remove the static_asserts, the template does not work with the zero-sized array.

Since zero-sized arrays are an extension, there surely is no rule about special treatment of them in the C++ standard, so my question is:
Do I miss something, is this a bug, or is this intended by the GCC authors?

#include <iostream>

template <size_t len>
void test1(const char(&arr)[len])
{
    static_assert(len > 0, "why am I never printed?");
}

template <size_t len>
void test2(const char(&arr)[len])
{
    static_assert(len > 10, "I will be printed");
}

int main()
{
    char arr5[5];
    test2(arr5);

    char arr0[0];
    test1(arr0);
}

Error output:

main.cpp: In function ‘int main()’:
main.cpp:21:15: error: no matching function for call to ‘test1(char [0])’
     test1(arr0);
               ^
main.cpp:21:15: note: candidate is:
main.cpp:4:6: note: template<unsigned int len> void test1(const char (&)[len])
 void test1(const char(&arr)[len])
      ^
main.cpp:4:6: note:   template argument deduction/substitution failed:
main.cpp: In instantiation of ‘void test2(const char (&)[len]) [with unsigned int len = 5u]’:
main.cpp:18:15:   required from here
main.cpp:12:5: error: static assertion failed: I will be printed
     static_assert(len > 10, "I will be printed");
     ^

My compiler version is:

g++ (Ubuntu 4.8.4-2ubuntu1~14.04) 4.8.4

Update: I tested it with Visual C++ 2015 today, it shows the same behaviour. VC++ supports zero-sized arrays only if they are the last member of a class/struct, but if the code is changed accordingly, it's exactly the same as with g++:

The function template never compiles with zero-sized arrays. Why?

#include <iostream>

struct s_arr
{
    char arr0[0];
};

template <size_t len>
void test(const char(&arr)[len])
{
}

int main()
{
    s_arr s;
    test1(s.arr0);
}
alain
  • 11,939
  • 2
  • 31
  • 51
  • 6
    Well, zero sized arrays aren't allowed anyway, so this isn't a big deal. – juanchopanza Nov 03 '15 at 22:15
  • This is why compiler extensions are bad. You could use `std::array` instead, which does allow zero-sized arrays. – M.M Nov 03 '15 at 22:16
  • It's an extension, I know. I thought it should work without surprises. – alain Nov 03 '15 at 22:17
  • 1
    And the ironic thing is that zero sized arrays were used in C++ 98 / 03 to implement `static_assert` before `static_assert` existed. See Alexandrescu's book on this. If the array had 0 size, the code refused to compile, which was a *good* thing. – PaulMcKenzie Nov 03 '15 at 22:23
  • I thought it's a fairly common extension, because I remember even Visual C++ 6.0 had it, but I'm not sure if it was generally allowed or only as the last element in a class/struct. – alain Nov 03 '15 at 22:26
  • @alain The "last element in a class/struct" is a 'C' hack to implement dynamic arrays -- it still works. However, I am almost sure that Visual C++ 6.0 didn't accept 0 sized arrays. If 0 sized arrays were common, then the home-made `static_assert`'s programmers were using before C++ 11 wouldn't work. – PaulMcKenzie Nov 03 '15 at 22:29
  • I know it did, I used it at work, there are still a few servers running it. But maybe it was only allowed as the last member of a struct. – alain Nov 03 '15 at 22:32
  • @alain http://stackoverflow.com/questions/3711233/is-the-struct-hack-technically-undefined-behavior – PaulMcKenzie Nov 03 '15 at 22:34
  • Yes, that's exactly how it was used where I worked, just that the size was `0`, so that `sizeof()` would not count the array. – alain Nov 03 '15 at 22:37
  • 2
    "were used in C++98" - by whom? The problem with zero-sized array extensions is easily avoided by using `-1` instead of `0` in the `static_assert`. I've always done it that way. – M.M Nov 03 '15 at 23:43

1 Answers1

1

Do I miss something, is this a bug, or is this intended by the GCC authors?

Accepting zero-sized arrays in templates would lead to either conformance issues or an impossible-to-maintain language.

Given

template <int N> void f(char (*)[N], int);
template <int N> void f(void *, void *);

A call f<1>(0, 0) must use the first overload because it is a better match for the second argument.

A call f<0>(0, 0) must use the second overload because SFINAE discards the first overload due to the zero-sized array.

Zero-sized arrays are allowed as an extension so long as they do not alter the semantics of any standard C++ program. Allowing zero-sized arrays during template argument substitution would alter the semantics of a standard C++ program, unless a whole list of exceptions gets implemented where zero-sized arrays should not be allowed.