Constexpr cast to const char []

It happened to many people, and it happened to me. I am stuck playing with compile-time strings in C ++.

I decided to use an unacceptable approach: using the template <char...> classes.

This is what I came up with, is very common, nothing special, and also does not work.

 template <char... chars> class string { public: static constexpr const char value[] = {chars...}; constexpr string() { } constexpr operator decltype(value) & () const { return value; } }; template <char... chars> constexpr const char string <chars...> :: value[]; 

My idea was to create a string instance of constexpr constructive and expose some kind of constexpr so that it would provide its contents.

Now if i do

 static constexpr const char x[] = "ciao"; template <const char * str> void print() { std :: cout << str << std :: endl; } print <x> (); 

It works and says ciao . I get ciao as well if I do

 std :: cout << string <'c', 'i', 'a', 'o'> {} << std :: endl; 

or

 print <string <'c', 'i', 'a', 'o', '\0'> :: value> (); 

But when I do

 print <string <'c', 'i', 'a', 'o', '\0'> {}> (); 

I get: No matching function for call to print .

I definitely missed something. Is it possible to do what I'm trying to do? Creating an instance does constexpr to somehow return a value ? If this worked, I could easily do operators and string manipulations at compile time, the "only" drawback is the ultra-boring 'i', 'n', 'i', 't', 'i', 'a', 'l', 'i', 'z', 'a', 't', 'i', 'o', 'n' .

Further experiments

I did another experiment that works great.

 template <char... chars> class string { public: constexpr string() { } constexpr operator size_t () const { return sizeof...(chars); } }; template <size_t length> void print() { std :: cout << length << std :: endl; } print <string <'c', 'i', 'a', 'o'> {}> (); 

And he prints pretty 4 .

+7
c ++ char templates constexpr
source share
2 answers

In C ++ 14, a restriction on template arguments for a non-piggy template parameter:

The template argument for a non-piggy template without template must be one of the following:
* for an atypical template parameter of an integral or enumerated type, the converted constant expression (5.19) of the type of the template parameter; or
* name of the asymmetric pattern; or
* constant expression (5.19), which denotes the address of a complete object with a static storage duration and external or internal communication or a function with external or internal communication, including function templates and function templates, but excluding non-static class members expressed (ignoring parentheses) like & id-expression, where id-expression is the name of an object or function, except that & can be omitted if the name refers to a function or array and should be omitted if the corresponding parameter template is a link; or
* constant expression that calculates the value of the null pointer (4.10); or
* constant expression, which calculates the value of the pointer of the zero element (4.11); or
* pointer to an element expressed as described in 5.3.1; or
* constant expression of type std::nullptr_t .

In your example string<'c', 'i', 'a', 'o', '\0'>{} there are none of them. Note that the first bullet is limited to an integer or enumerated type, and const char* is not one of them (the exception for integral types is that it allows you to compile the "Additional experiments" example). None of the other bullets are used. Therefore, the appropriate C ++ 14 should reject your code.

The workaround is to simply pass the underlying array of characters directly:

 print<string<'c', 'i', 'a', 'o', '\0'>::value>(); 

Or take the type itself as an argument to the template type and type ::value inside the function:

 template <class T> void print() { std::cout << T::value << std::endl; } print<string<'c', 'i', 'a', 'o', '\0'>>(); 

Or...


You will be happy to know that in C ++ 17 things are getting better. See N4198 for an example of motivation:

 template<int *p> struct A {}; int n; A<&n> a; // ok constexpr int *p() { return &n; } A<p()> b; // error 

The document suggests fewer restrictions on non-type template arguments, which would make the above example well-formed. The new wording reads:

The template argument for a parameter of type non-type must be a constant constant expression (5.20) of the type of the template parameter. For a template type of a non-peak type of a reference type or a pointer type, the value of a constant expression should not be referenced (or for a pointer type, it should not be an address):
* subobject (1.8),
* temporary object (12.2),
* string literal (2.14.5),
* the result of the expression typeid (5.2.8) or * the predefined variable __func__ (8.4.1).

What is it. Everything else is allowed.

Since string<'c', 'i', 'a', 'o', '\0'>{} converted to a pointer that is not one of these things, the example becomes well-formed. Clang 3.8 will compile your example in C ++ 1z mode (correct), but not in C ++ 14 mode (correct). GCC has not yet implemented this. Just give them time.

+4
source share

User-defined transformations are not taken into account when matching template arguments.

However instead

 print <string <'c', 'i', 'a', 'o', '\0'>{} > (); 

& hellip; you can just write

 print <string <'c', 'i', 'a', 'o', '\0'>::value> (); 

Or you can define

 template <const char* str> void print() { std::cout << str << std::endl; } template< class Type > void print() { print<Type::value>(); } 

& hellip; and then just write

 print <x> (); print <string <'c', 'i', 'a', 'o', '\0'>> (); 

& hellip; where x is the area of ​​your namespace

 static constexpr const char x[] = "ciao"; 

Note : this code is compiled using MinGW g ++ 5.1.0 with -std=c++14 , but not with Visual C ++ 2015 update 2. Such differences are common for code that pushes the boundaries of the language. The standard is in stream, and compilers are evolving to try to keep up with the standard, and therefore such code is not very portable in practice.

-one
source share

All Articles