Reputation: 455
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.
Upvotes: 4
Views: 566
Reputation: 23001
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
.
Upvotes: 4
Reputation:
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.
Upvotes: 2