Using constexpr wrong from the c++11 standard or have I caught some compiler bug?

c++11, clang++, constexpr, g++

Solution

Update:

GCC chokes on your function `stringlength`. Try this:

constexpr size_t stringlength(char const* str, size_t i=0)
{
    return str ? (str[i] ? 1 + stringlength(str, i+1) : 0) : 0;
}

To GCC 4.6.3 this seems dubious:

static constexpr int const* c = &a[0];
static constexpr int const* d = &b[0];

Try this:

static constexpr int* c = &a[0];
static constexpr int* d = &b[0];

Old answer:

You code seems to be correct and with GCC 4.6.3 it also compiles.

int main()
{
    const char* a = "hallo";
    const char* b = "qallo";
    std::cout << is_equal(a,b,5) << std::endl;
    constexpr const char* c = "hallo";
    std::cout << is_equal(a,c,5) << std::endl;
}

Be careful, that the strings you are giving to your functions are constants.

See here what is allowed with `constexpr`.

Problem

I'm playing with the constexpr keyword and coded the following simple program: ``` #include <iostream> using namespace std; template<typename T> constexpr bool is_equal(T const* array1, T const* array2, size_t len) { return array1 == array2 || (len ? array1[len - 1] == array2[len - 1] && is_equal<T>(array1, array2, len - 1) : true); } template<typename T, size_t N1, size_t N2> constexpr bool is_equal(T const (&array1)[N1], T const (&array2)[N2]) { return N1 == N2 && is_equal<T>(array1, array2, N1); } template<typename T, size_t N> constexpr size_t arraylength(T const (&array)[N]) { return N; } constexpr size_t stringlength(char const* str, size_t len=0) { return str ? (*str ? stringlength(str + 1, len + 1) : len) : 0; } constexpr size_t min(size_t one, size_t another) { return one < another ? one : another; } constexpr size_t min_length(char const* str1, char const* str2) { return min(stringlength(str1), stringlength(str2)); } template<typename T, size_t N1, size_t N2> constexpr size_t min_length(T const (&array1)[N1], T const (&array2)[N2]) { return min(N1, N2); } template<bool cond=false> struct to_num { enum {value=0}; }; template<> struct to_num<true> { enum {value=42}; }; template<size_t init> struct two_times { enum {value=init*2}; }; static constexpr char x[]{"One string"}; static constexpr char y[]{"One string"}; static constexpr int a[]{1,2,3,4}; static constexpr int b[]{1,2,3,4}; static constexpr int const* const c = &a[0]; static constexpr int const* const d = &b[0]; int main() { cout << "The two literals are equal: " << to_num< is_equal("One string", "One string") >::value << endl; // COMPILES AND WORKS IN GCC BUT NOT IN CLANG cout << "The two variables x & y are equal: " << to_num< is_equal(x, y) >::value << endl; cout << "Pointers a & c are equal: " << to_num< a == c >::value << endl; cout << "Pointers b & c are equal: " << to_num< b == c >::value << endl; cout << "Pointers c & d are equal: " << to_num< c == d >::value << endl; cout << "The contents of c & d is the same: " << to_num< is_equal(c, d, arraylength(a)) >::value << endl; cout << "Pointers a & b are equal: " << to_num< a == b >::value << endl; cout << "The contents of a & b is the same: " << to_num< is_equal(a, b) >::value << endl; cout << "String x contains " << two_times< stringlength(x) >::value / 2 << " characters" << endl; // COMPILES AND WORKS IN CLANG BUT NOT IN GCC cout << "String literal contains " << two_times< stringlength("literal") >::value / 2 << " characters" << endl; // COMPILES AND WORKS IN CLANG BUT NOT IN GCC cout << "Array literal contains " << two_times< arraylength("literal") >::value / 2 << " values" << endl; return 0; } ``` As the comments remark, some code compiles and works ok with g++ but not with clang++ and other compiles and work ok with clang++ but not with g++ The gcc error is: ``` comaprison.cpp: In function ‘int main()’: comaprison.cpp:97:62: in constexpr expansion of ‘stringlength(((const char*)(& x)), 0ul)’ comaprison.cpp:29:55: in constexpr expansion of ‘stringlength((str + 1u), (len + 1ul))’ comaprison.cpp:97:64: error: ‘((((const char*)(& x)) + 1u) != 0u)’ is not a constant expression cout << "String x contains " << two_times< stringlength(x) >::value / 2 << " characters" << endl; ^ comaprison.cpp:97:64: note: in template argument for type ‘long unsigned int’ comaprison.cpp:98:76: in constexpr expansion of ‘stringlength(((const char*)"literal"), 0ul)’ comaprison.cpp:29:55: in constexpr expansion of ‘stringlength((str + 1u), (len + 1ul))’ comaprison.cpp:98:78: error: ‘((((const char*)"literal") + 1u) != 0u)’ is not a constant expression cout << "String literal contains " << two_times< stringlength("literal") >::value / 2 << " characters" << endl; // COMPILES AND WORKS IN CLANG BUT NOT IN GCC ^ comaprison.cpp:98:78: note: in template argument for type ‘long unsigned int’ ``` and the clang++ one is: ``` comaprison.cpp:89:55: error: non-type template argument is not a constant expression cout << "The two literals are equal: " << to_num< is_equal("One string", "One string") >::value << ... ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ comaprison.cpp:8:19: note: subexpression not valid in a constant expression return array1 == array2 || (len ? array1[len - 1] == array2[len - 1] && is_equal<T>(array1, array2... ^ comaprison.cpp:14:24: note: in call to 'is_equal(&"One string"[0], &"One string"[0], 11)' return N1 == N2 && is_equal<T>(array1, array2, N1); ^ comaprison.cpp:89:55: note: in call to 'is_equal("One string", "One string")' cout << "The two literals are equal: " << to_num< is_equal("One string", "One string") >::value << ... ^ 1 error generated. ``` g++ is "gcc version 4.8.1 (Ubuntu/Linaro 4.8.1-10ubuntu9)", clang++ is "Ubuntu clang version 3.4-1ubuntu1 (trunk) (based on LLVM 3.4)" Both in a x86_64 linux. The command lines are: ``` clang++ -std=c++11 -o comaprison comaprison.cpp g++ -std=c++11 -o comaprison comaprison.cpp ``` So, am I doing anything outside the c++11 standard with this code or there is something wrong in both compilers?. Note that If I remove the g++ problematic lines but leave the clang++ problematic one, the code compiles and works with g++ and if I remove the clang++ problematic line and leave the g++ problematic ones, the code compiles and works ok with clang++ Note also that I'm using the two templated structs to force the compiler to resolve the functions at compile time. Thank you for your time and experience.

Original source