Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

In which case will C++ do array bounds checking at compile time?

Inspired by the idea of "C++ HTML template engine that uses compile time HTML parsing", I am trying to write a sample class to check whether the first char in a string is a.

int dummy[0];
class Test
{
public:
    constexpr Test(const char *p):p_(p){}
    constexpr void check()const
    {
        if (p_[0]!='a')
            dummy[1]=0;
    }
    const char *p_;
};


constexpr Test operator"" _test(const char *pszText, size_t)
{
  Test t(pszText);
  t.check();
  return t;
}


int main()
{
    //dummy[1] = 0;
    constexpr Test t = "baa"_test;
}

It works well(GCC7.1 on Ubuntu). If the first char is not a, it will give a compile error:

main.cpp:29:24: error: array subscript value ‘1’ is outside the bounds
of array ‘dummy’ of type ‘int [0]’
constexpr Test t = "baa"_test;

What confused me is that if I change the code to:

int main()
{
    dummy[1] = 0; //no compile error anymore
}

I am wondering when C++ reports an array out of bounds error.

like image 716
camino Avatar asked Oct 18 '22 08:10

camino


1 Answers

constexpr requires that the compiler evaluates the code. And, because, in the context of the C++ abstract machine, dummy[1] is undefined behavior, it must emit a diagnostic. (This can be either a warning or an error. However, both g++ and clang++ choose to make this an error.)

However, just because dummy[1] is undefined behavior in the context of the C++ abstract machine, doesn't mean that something else isn't defining the behavior. This means that the code should not necessarily be rejected (if the compiler itself doesn't need to use it). It simply is undefined behavior, not an error.

For instance, compiler writers will use out of bounds array accesses, and because they're writing the compiler, they can ensure that the behavior that occurs is the one desired. A good example is array memory management, where the pointer returned is typically one word past the beginning of the allocated memory (because the size also needs to be allocated.) So when the compiler writer needs to read the size he or she may do ((size_t const*)ptr)[-1].

That being said, a better way to trigger a compiler error in this case is to simply use a static assert: static_assert(p_[0]!='a', "String does not begin with 'a'.").

like image 146
OmnipotentEntity Avatar answered Oct 21 '22 09:10

OmnipotentEntity