A constexpr function accepts only parameters, which are of a literal type. An object of the class string is not a literal type itself, but a pointer (which is a scalar type) or a reference is a literal type. And therefore a reference to string is a literal type.
Furthermore a constexpr function returns only a constant expression, if it's arguments are also constant expressions.
"The scale* function will return a constant expression if its argument is a constant expression but not otherwise:"
Source: The C++ Primer, 5th edition
scale is in this case is_shorter()
Now I'm passing here two references to string to a constexpr function, which doesn't remain at a fixed address (global or static) but at a variable address (local). Therefore both arguments shouldn't be constant expressions.
The result of the constexpr function is assigned to a constexpr bool.
#include <iostream>
#include <string>
#include <type_traits>
using namespace std;
/*
* it isn't possible to define is_shorter as constexpr function
* 1. string is not a literal type, but can be passed as pointer or reference,
* which are literal types.
* 2. the member function size() is not a constexpr function
*
*/
// compare the length of two strings
constexpr bool is_shorter(const string& s1, const string& s2) {
// compiler message from gcc: 'error: call to non-constexpr function'
// return s1.size() < s2.size();
// compare addresses of pointers, which is legal or illegal but useless, because
// both parameters are local objects and not stored at fixed addresses.
return &s1 != &s2;
}
int main() {
string shrt = "short";
string longer = "longer";
cout << "address of shrt " << &shrt << "\n";
cout << "address of longer " << &longer << "\n";
constexpr bool state = is_shorter(shrt, longer);
if (state) {
cout << "shrt is shorter\n";
} else {
cout << "longer is longer\n";
}
cout << "string " << is_literal_type<string>::value << "\n";
cout << "string& " << is_literal_type<string&>::value << "\n";
cout << "string* " << is_literal_type<string*>::value << "\n";
return 0;
}
Compile:
$ g++ -o ex646 ex646.cpp -std=gnu++11 -Wall -Wpedantic
Run:
$ ./ex646
address of shrt 0x7ffd39e41a50 # every time different
address of longer 0x7ffd39e41a40 # every time different, always 0x10 more than &shrt
shrt is shorter
string 0
string& 1
string* 1
How is the compiler able to compare the addresses of both strings? They are during every run-time of the program different, whereas their relative position remains constant. Is the key to this usage, that at least their relative position to each other remains constant?
Update 2015-08-12
As time of writing this looks like in 'a bug in the constant evaluation rules' of the ISO-Standard (correct me if I'm wrong). See this discussion on an isocpp.org list. And therefore this shouldn't be a compiler bug inside GCC and CLANG.
Thanks dyp!