Home > Enterprise >  Conditional compilation (constexpr if) and "ISO C forbids zero-size array"
Conditional compilation (constexpr if) and "ISO C forbids zero-size array"

Time:02-02

With the following code:

#include <algorithm>

constexpr int DATA_SIZE = 5;

constexpr int A_ARRAY_ALLOWED_SIZE = 5;
constexpr int A_ARRAY_SIZE = std::min(A_ARRAY_ALLOWED_SIZE, DATA_SIZE);
constexpr int B_ARRAY_SIZE = DATA_SIZE - A_ARRAY_ALLOWED_SIZE;

class A {
    int a[A_ARRAY_SIZE];
};

class B {
    int b[B_ARRAY_SIZE];
};

int main()
{
    A a;
    
    if constexpr (B_ARRAY_SIZE)
    {
        B b;
    }

    return 0;
}

I'm getting compiler error (with -pedantic flag) which complains that zero-size array is not allowed. In my example the object with the zero size array is never created but looks like it is still an issue.

I was trying to workaround it with usage of std::conditional but even then I ended up with an additional function like:

constexpr int Get_B_ARRAY_SIZE()
{
    if (B_ARRAY_SIZE)
        return B_ARRAY_SIZE;
    return 1; // workaround for zero-size array
}

What is a proper way of handling such an issue?

EDIT: I'm aware that all of if branches should contain valid code. I'm also aware that zero-size arrays are not allowed. My question is how to refactor this code to get similar behawior like when compiling without -pedantic flag. I suspect that I can use template meta programming to achieve this purpose but I'm not sure how to do it.

CodePudding user response:

If you need equivalent of std::conditional, but for values, rather than types, you can do it like this:

#include <iostream>
#include <type_traits>

template<size_t N>
struct safe_array_size : std::integral_constant<size_t, N> {};

template<>
struct safe_array_size<0> : std::integral_constant<size_t, 1> {};

int main()
{
    char a[safe_array_size<0>::value];
    char b[safe_array_size<1>::value];
    std::cout << sizeof(a) << std::endl;
    std::cout << sizeof(b) << std::endl;
}

Or using std::conditional:

#include <iostream>
#include <type_traits>

template<size_t N>
constexpr size_t safe_array_size = std::conditional_t<N==0, std::integral_constant<size_t, 1>, std::integral_constant<size_t, N>>::value;

int main()
{
    char a[safe_array_size<0>];
    char b[safe_array_size<1>];
    std::cout << sizeof(a) << std::endl;
    std::cout << sizeof(b) << std::endl;
}

CodePudding user response:

if constexpr (at least, how you are using it) cannot directly work around this error because it is the class definition that is ill-formed. Whether or not you instantiate the class is irrelevant. You can fix this by ensuring that the array size is never zero with std::max:

#include <algorithm>

constexpr int DATA_SIZE = 5;

constexpr int A_ARRAY_ALLOWED_SIZE = 5;
constexpr int A_ARRAY_SIZE = std::min(A_ARRAY_ALLOWED_SIZE, DATA_SIZE);
constexpr int B_ARRAY_SIZE = DATA_SIZE - A_ARRAY_ALLOWED_SIZE;

class A {
    int a[A_ARRAY_SIZE];
};

class B {
    int b[std::max(B_ARRAY_SIZE, 1)];
};

int main()
{
    A a;
    
    if constexpr (B_ARRAY_SIZE)
    {
        B b;
    }

    return 0;
}

Note that std::max is constexpr as of C 14. You could implement your own max function if you are on C 11.

If you need to ensure that the class is never actually instantiated unless the array size is non-zero, the if constexpr check in the above code will handle that.

  • Related