constexpr char array with GCC and clang - c++

Consider the following code:
#include <cstddef>
#include <iostream>
#include <stdexcept>
class const_string {
public:
template <std::size_t sz>
constexpr const_string(const char (&str)[sz]): p_(str) {}
constexpr char operator[](std::size_t i) const { return p_[i]; }
private:
const char* p_;
};
template <char c>
void Print() { std::cout << c << '\n'; }
int main() {
constexpr char str[] = "Hello World";
Print<const_string(str)[0]>();
}
It compiles fine with clang, while GCC gives the following error message:
in constexpr expansion of 'const_string((* & str)).const_string::operator[](0ul)'
error: '(const char*)(& str)' is not a constant expression
However, if I change Print<const_string(str)[0]>(); to Print<const_string("Hello World")[0]>();. Both clang and GCC compile fine.
What is going on here? Which compiler is correct according to the standard?

It's a bug and appears to compile on gcc 5 as shown here.

Related

constexpr std::string_view::find_last_of doesn't work on clang 8 with libstdc++ 9

The following code compiles under g++ 9, with the flag -std=c++17, but not clang 8 with the same flag:
#include <string_view>
#include <cstdlib>
int main() {
constexpr std::string_view hello = "hello";
constexpr size_t last_l = hello.find_last_of("lo");
}
The error message is as follows:
test.cpp:8:19: error: constexpr variable 'last_l' must be initialized by a constant expression
constexpr size_t last_l = hello.find_last_of("lo");
^ ~~~~~~~~~~~~~~~~~~~~~~~~
/usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/bits/char_traits.h:349:9: note: cast from 'void *' is not allowed in a constant expression
return static_cast<const char_type*>(__builtin_memchr(__s, __a, __n));
^
/usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/bits/string_view.tcc:150:12: note: in call to 'find(&"lo"[0], 2, "hello"[4])'
if (traits_type::find(__str, __n, this->_M_str[__size]))
^
/usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/string_view:402:22: note: in call to '&hello->find_last_of(&"lo"[0], 18446744073709551615, 2)'
{ return this->find_last_of(__str, __pos, traits_type::length(__str)); }
^
test.cpp:8:34: note: in call to '&hello->find_last_of(&"lo"[0], 18446744073709551615)'
constexpr size_t last_l = hello.find_last_of("lo");
^
test.cpp:10:16: error: static_assert expression is not an integral constant expression
static_assert(last_l == 3);
^~~~~~~~~~~
test.cpp:10:16: note: initializer of 'last_l' is not a constant expression
test.cpp:8:19: note: declared here
constexpr size_t last_l = hello.find_last_of("lo");
^
2 errors generated.
Looking at the error message, it looks like it calls std::char_traits<char>::find, which according to cppreference should be constexpr and not a problem. However, looking at the implementation in char_traits.h, while it it marked constexpr, it does not seem to follow constexpr rules:
static _GLIBCXX17_CONSTEXPR const char_type*
find(const char_type* __s, size_t __n, const char_type& __a)
{
if (__n == 0)
return 0;
#if __cplusplus >= 201703L
if (__builtin_constant_p(__n)
&& __builtin_constant_p(__a)
&& __constant_char_array_p(__s, __n))
return __gnu_cxx::char_traits<char_type>::find(__s, __n, __a);
#endif
return static_cast<const char_type*>(__builtin_memchr(__s, __a, __n));
}
However, g++ does not seem to have a problem with that, even though it seems from the clang diagnostic message __builtin_memchr returns a void*, which is then casted to a char*, which seems to be disallowed (see bullet 14).
Following this same train of thought, the following sample also compiles with g++ but not clang:
#include <string>
int main() {
constexpr const char* asdf = "asdf";
constexpr const char* s_ptr = std::char_traits<char>::find(asdf, 2, 's');
}
With the error message:
test_traits.cpp:5:24: error: constexpr variable 's_ptr' must be initialized by a constant expression
constexpr const char* s_ptr = std::char_traits<char>::find(asdf, 2, 's');
^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/bits/char_traits.h:349:9: note: cast from 'void *' is not allowed in a constant expression
return static_cast<const char_type*>(__builtin_memchr(__s, __a, __n));
^
test_traits.cpp:5:32: note: in call to 'find(&"asdf"[0], 2, 's')'
constexpr const char* s_ptr = std::char_traits<char>::find(asdf, 2, 's');
^
1 error generated.
This seems like both a stdc++ bug, by not having std::char_traits<_>::find be properly constexpr, and a g++ bug, for compiling it.
Is there something I'm missing? Also, is there a workaround? I came across this trying to use magic enum, and would like to use it in a clang environment.
Update (6/7/19)
As pointed out by #cpplearner, it seems like clang should be taking the return __gnu_cxx::char_traits<char_type>::find(__s, __n, __a); Interestingly enough, that doesn't seem to be the problem. The following code, which is the exact same function from std::char_traits<char>::find, except without that constant branch, still compiles fine in g++, which makes me wonder if g++ is even taking that path:
#include <cstddef>
static constexpr const char*
find_noconst(const char* __s, size_t __n, const char& __a)
{
if (__n == 0)
return 0;
return static_cast<const char*>(__builtin_memchr(__s, __a, __n));
}
int main() {
constexpr const char* asdf = "asdf";
constexpr const char* s_ptr = find_noconst(asdf, 2, 's');
}
This still compiles in g++, and fails in clang with the same error.
Update 2 (6/7/19)
More research confirms #cpplearner comment:
#include <cstddef>
#include <string>
#include <iostream>
static constexpr const char*
branch_test(const char* __s, size_t __n, const char& __a)
{
if (__n == 0)
return 0;
if (__builtin_constant_p(__n)
&& __builtin_constant_p(__a)
&& std::__constant_char_array_p(__s, __n))
return "took branch";
return "did not take branch";
}
int main() {
constexpr const char* test = branch_test("asdf", 2, 's');
std::cout << test << std::endl;
}
With gcc, this prints took branch, but with clang it prints did not take branch.
Refining it more:
#include <string>
#include <iostream>
int main() {
constexpr bool test = std::__constant_char_array_p("test", 2);
std::cout << test << std::endl;
}
Prints 0 on clang and 1 on g++.
After digging into that implementation, it seems that this is actually a clang bug. The most minimal example of difference I could find was this:
#include <iostream>
constexpr bool is_const_char(const char* a, size_t idx) {
return __builtin_constant_p(a[idx]);
}
int main() {
constexpr bool test = is_const_char("test", 0);
std::cout << test << std::endl;
}
Prints 0 on clang and 1 on gcc. The most interesting part to me, is what actually works:
#include <iostream>
int main() {
constexpr const char* str = "test";
constexpr bool test1 = __builtin_constant_p(str[0]);
constexpr bool test2 = __builtin_constant_p("test"[0]);
std::cout << test1 << ", " << test2 << std::endl;
}
That program consistently prints 1, 1 on both clang and g++.

Overload rules for multiple, templated constructors in list initialization

I'm unsure if the following code is valid according to the c++11 standard and should have the same behavior across different implementations or not:
#include <cstddef>
struct Foo{
template <std::size_t N>
constexpr Foo( const char ( &other )[N] )
{}
template <class T>
constexpr Foo( const T* const& other ) = delete;
};
struct Bar {
Foo a;
int b;
};
int main() {
Bar bar{ "Hello",5};
}
The general Idea is to allow the construction from a string literal and a std::string (not shown here), but not from a pointer to const char, which is somewhat tricky (discussed in this question).
Newer versions of g++ (>=6.0) and almost all clang++ versions(>=3.4) seem to compile this just fine, but e.g. with g++-4.8 -std=c++11 main.cpp I get the following error:
main.cpp: In function ‘int main()’:
main.cpp:17:27: error: use of deleted function ‘constexpr Foo::Foo(const T* const&) [with T = char]’
Bar bar{ "Hello",5};
So my question is:
Does the standard require a certain behavior for this code at all and if so, who is right?
Enclosing the initalizer in {} worked for me, like this:
#include <cstddef>
struct Foo {
template<std::size_t N>
constexpr Foo(const char (&)[N]) {}
template<class T>
constexpr Foo(const T* const&) = delete;
};
struct Bar {
Foo a;
int b;
};
int main() {
Bar bar{ {"Hello"}, 5 };
// ^^^^^^^^^
(void)bar;
}
I tested it on wandbox with GCC 4.8.1
https://wandbox.org/permlink/1TJF2NyT7mrKkqQ0
GCC is not necessarily incorrect here, I vaguely recall
a defect report regarding this.

Compilation errors with clang when using a custom trait and a c++11 enum of type bool

The following code compiles fine with g++ and fails with clang (all versions I've tested):
#include <iostream>
namespace has_insertion_operator_impl
{
typedef char no;
typedef char yes[2];
struct any_t
{
template <typename T>
any_t(const T&);
};
yes& testStreamable(std::ostream&);
no testStreamable(no);
no operator<<(const std::ostream&, const any_t&);
template <typename T>
struct has_insertion_operator
{
static std::ostream& s;
static const T& t;
static const bool value = sizeof(testStreamable(s << t)) == sizeof(yes);
};
} // namespace has_insertion_operator_impl
template <typename T>
struct has_insertion_operator : has_insertion_operator_impl::has_insertion_operator<T>
{};
enum A : bool {
Yup = true,
Nop = false,
};
template <typename T>
bool getTraitVal(const T&) { return has_insertion_operator<T>::value; }
int main() { std::cout << getTraitVal(A::Yup) << std::endl; }
The error (with clang only!) is this:
prog.cc:24:59: error: use of overloaded operator '<<' is ambiguous (with operand types 'std::ostream' (aka 'basic_ostream<char>') and 'const A')
static const bool value = sizeof(testStreamable(s << t)) == sizeof(yes);
I believe this is a small enough example. Here are links to online compilers for it:
clang 3.8
g++ 6.1
When I change the enum type from bool to int - the error disappears.
So why is this happening? This was originally discovered when using the doctest and Catch testing frameworks - here is the bug report for Catch. Could it be a clang bug?
I know it doesn't answer your question, but it seems clang has a problem with enums of underlying type 'bool'.
I further reduced your example to:
#include <iostream>
enum A : bool {
Yup = true,
Nop = false,
};
int main() {
A t = Yup;
std::cout << t;
}
And here you can already have a feeling for what's happening:
prog.cc:10:15: error: use of overloaded operator '<<' is ambiguous (with operand types 'ostream' (aka 'basic_ostream<char>') and 'A')
std::cout << t;
~~~~~~~~~ ^ ~
/usr/local/libcxx-3.8/include/c++/v1/ostream:195:20: note: candidate function
basic_ostream& operator<<(bool __n);
^
/usr/local/libcxx-3.8/include/c++/v1/ostream:198:20: note: candidate function
basic_ostream& operator<<(int __n);
^
...

How can I see the type deduced for a template type parameter?

Is there an easy way to force compilers to show me the type deduced for a template parameter? For example, given
template<typename T>
void f(T&& parameter);
const volatile int * const pInt = nullptr;
f(pInt);
I might want to see what type is deduced for T in the call to f. (I think it's const volatile int *&, but I'm not sure.) Or given
template<typename T>
void f(T parameter);
int numbers[] = { 5, 4, 3, 2, 1 };
f(numbers);
I might want to find out if my guess that T is deduced to be int* in the call to f is correct.
If there's a third-party library solution (e.g., from Boost), I'd be interested to know about it, but I'd also like to know if there's an easy way to force a compilation diagnostic that would include the deduced type.
Link time solution:
On my platform (OS X), I can get the linker to give me this information by simply making a short program that is complete, minus the definition of the function I'm curious about:
template<typename T>
void f(T&& parameter); // purposefully not defined
int
main()
{
const volatile int * const pInt = nullptr;
f(pInt);
}
Undefined symbols for architecture x86_64:
"void f<int const volatile* const&>(int const volatile* const&&&)", referenced from:
_main in test-9ncEvm.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
Admittedly I get the "triple reference", which should be interpreted as an lvalue reference (due to reference collapsing), and is a demangling bug (perhaps I can get that fixed).
Run time solution:
I keep a type_name<T>() function handy for this type of thing. A completely portable one is possible, but sub-optimal for me. Here it is:
#include <type_traits>
#include <typeinfo>
#include <string>
template <typename T>
std::string
type_name()
{
typedef typename std::remove_reference<T>::type TR;
std::string r = typeid(TR).name();
if (std::is_const<TR>::value)
r += " const";
if (std::is_volatile<TR>::value)
r += " volatile";
if (std::is_lvalue_reference<T>::value)
r += "&";
else if (std::is_rvalue_reference<T>::value)
r += "&&";
return r;
}
I can use it like:
#include <iostream>
template<typename T>
void f(T&& parameter)
{
std::cout << type_name<T>() << '\n';
}
int
main()
{
const volatile int * const pInt = nullptr;
f(pInt);
}
which for me prints out:
PVKi const&
That's not terribly friendly output. Your experience may be better. My platform ABI is based on the Itanium ABI. And this ABI includes this function:
namespace abi
{
extern "C"
char*
__cxa_demangle(const char* mangled_name, char* buf, size_t* n, int* status);
}
I can use this to demangle C++ symbols into a human readable form. An updated type_name<T>() to take advantage of this is:
#include <type_traits>
#include <typeinfo>
#include <string>
#include <memory>
#include <cstdlib>
#include <cxxabi.h>
template <typename T>
std::string
type_name()
{
typedef typename std::remove_reference<T>::type TR;
std::unique_ptr<char, void(*)(void*)> own
(
abi::__cxa_demangle(typeid(TR).name(), nullptr, nullptr, nullptr),
std::free
);
std::string r = own != nullptr ? own.get() : typeid(TR).name();
if (std::is_const<TR>::value)
r += " const";
if (std::is_volatile<TR>::value)
r += " volatile";
if (std::is_lvalue_reference<T>::value)
r += "&";
else if (std::is_rvalue_reference<T>::value)
r += "&&";
return r;
}
And now the previous main() prints out:
int const volatile* const&
I have tried the followings with g++ 4.7.2 and clang++ 3.4 (trunk 184647); they both give
a compile-time error and the error message is containing the deduced type.
I have no access to MSVC 12, please check what happens and provide feedback.
#include <string>
template <typename T>
struct deduced_type;
template<typename T>
void f(T&& ) {
deduced_type<T>::show;
}
int main() {
f(std::string()); // rvalue string
std::string lvalue;
f(lvalue);
const volatile int * const pInt = nullptr;
f(pInt);
}
The error messages: g++ 4.7.2
error: incomplete type deduced_type<std::basic_string<char> > used in nested name specifier
error: incomplete type deduced_type<std::basic_string<char>&> used in nested name specifier
error: incomplete type deduced_type<const volatile int* const&> used in nested name specifier
and clang++
error: implicit instantiation of undefined template deduced_type<std::basic_string<char> >
error: implicit instantiation of undefined template deduced_type<std::basic_string<char> &>
error: implicit instantiation of undefined template deduced_type<const volatile int *const &>
The note / info messages also contain the type of f with both compilers, for example
In instantiation of void f(T&&) [with T = std::basic_string<char>]
It's butt-ugly but works.
To get the compiler to show you the type of a variable (perhaps in a round about way);
T parameter;
....
void f(int x);
...
f(parameter);
compiler should complain that "T" cannot be converted to int, assuming that it actually can't.
A slightly more succinct way to trigger the compiler diagnostic from Ali's answer is with a deleted function.
template <typename T>
void f(T&&) = delete;
int main() {
const volatile int * const pInt = nullptr;
f(pInt);
return 0;
}
With GCC 8.1.0, you get:
error: use of deleted function 'void f(T&&) [with T = const volatile int* const&]

Compiler error when using constexpr and lambda

I encountered a problem when using constexpr functions together with lambdas.
The following code is a minimal version which reproduces the error:
#include <iostream>
constexpr unsigned bar(unsigned q) {
return q;
}
template<unsigned N>
unsigned foo() {
return N;
}
template<typename F>
void print(F f) {
std::cout << f() << std::endl;
}
template<unsigned Q>
int stuff() {
constexpr unsigned n = bar(Q);
print([]() { return foo<n>(); });
}
int main() {
stuff<13>();
}
When compiling with gcc version 4.6.3 (Ubuntu/Linaro 4.6.3-1ubuntu5) there are the following compiler errors:
constexpr_template.cpp: In lambda function:
constexpr_template.cpp:24:9: instantiated from ‘stuff() [with unsigned int Q = 13u]::<lambda()>’
constexpr_template.cpp:24:2: instantiated from ‘int stuff() [with unsigned int Q = 13u]’
constexpr_template.cpp:29:12: instantiated from here
constexpr_template.cpp:24:32: error: no matching function for call to ‘foo()’
constexpr_template.cpp:24:32: note: candidate is:
constexpr_template.cpp:9:10: note: template<unsigned int N> unsigned int foo()
Now the strange part is, if constexpr unsigned n = bar(Q); is changed into constexpr unsigned n = Q; it works.
What is also working is print([]() { return foo<bar(Q)>(); });...
Is this a bug in GCC or what am I doing wrong?
Gcc 4.6 was the first version to support constexpr, and it is not unusual for minor bugs to be present upon release of such features. You can verify from this Live Example on Coliru that gcc 4.8.1 and Clang 3.4 SVN correctly parse your code. You probably should upgrade your compiler accordingly.