Question

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.

Was it helpful?

Solution

Update:

  1. 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;
    }
    
  2. 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.

OTHER TIPS

Your arraylength example that you say doesn't work with GCC, works just fine with GCC 4.7 and 4.8, and except for an unrelated error about constexpr const, also with GCC 4.6.

As for your stringlength example: this is a limitation of GCC. I know I've seen a bug report about it already. You can work around it by rewriting your stringlength function:

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

but as far as I can tell, what you had already was perfectly valid.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top