Constexpr cast to const char[]

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

I decided to take the apparently unusable approach: using template <char...> classes.

This is what I came up with, it is very common, nothing special, and also it 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 making a string instance constexpr constructible and exposing some kind of constexpr casting so that it would provide its content.

Now, if I do

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

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

print <x> ();

This works and says ciao . I get ciao also if I do

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

or

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

But when I do

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

I get: No matching function for call to print .

I am definitely missing something. Is it unfeasible to do what I am trying to do? Making an instance do a constexpr cast to somehow return value ? If that worked, I would be able to easily make operators and string manipulation at compile time, the "only" downside being 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 perfectly.

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 it prints out a pretty 4 .


In C++14, the restriction on template arguments to a template non-type parameter is:

A template-argument for a non-type, non-template template-parameter shall be one of:
* for a non-type template-parameter of integral or enumeration type, a converted constant expression (5.19) of the type of the template-parameter; or
* the name of a non-type template-parameter; or
* a constant expression (5.19) that designates the address of a complete object with static storage duration and external or internal linkage or a function with external or internal linkage, including function templates and function template-ids but excluding non-static class members, expressed (ignoring parentheses) as & id-expression, where the id-expression is the name of an object or function, except that the & may be omitted if the name refers to a function or array and shall be omitted if the corresponding template-parameter is a reference; or
* a constant expression that evaluates to a null pointer value (4.10); or
* a constant expression that evaluates to a null member pointer value (4.11); or
* a pointer to member expressed as described in 5.3.1; or
* a constant expression of type std::nullptr_t .

In your example, string<'c', 'i', 'a', 'o', ''>{} is none of those. Note that the first bullet is restricted to integral or enumeration type, and const char* is neither of those (the exception for integral types is what allows your "Further experiments" example to compile). None of the other bullets apply. So a conforming C++14 should reject your code.

The workaround is to simply pass the underlying character array directly:

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

Or to take the type itself as the template type argument, and print ::value within the function:

template <class T>
void print() {
    std::cout << T::value << std::endl;
}

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

Or...


You'll be happy to know that in C++17, things get better. See N4198, with the motivating example:

template<int *p> struct A {};
int n;
A<&n> a; // ok

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

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

A template-argument for a non-type template-parameter shall be a converted constant expression (5.20) of the type of the template-parameter. For a non-type template-parameter of reference type or pointer type, the value of the constant expression shall not refer to (or for a pointer type, shall not be the address of):
* a subobject (1.8),
* a temporary object (12.2),
* a string literal (2.14.5),
* the result of a typeid expression (5.2.8), or * a predefined __func__ variable (8.4.1).

That's it. Everything else is allowed.

Since string<'c', 'i', 'a', 'o', ''>{} converts to a pointer that is none of those things, the example becomes well-formed. Clang 3.8 will compile your example in c++1z mode (correctly) but not in c++14 mode (correctly). GCC does not implement this yet. Just give them time.


User defined conversions are not considered for template argument matching.

However, instead of

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

… you can just write

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

Or you can define

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

template< class Type >
void print()
{
    print<Type::value>();
}

… and then write just

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

… where x is your namespace scope

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

Note : this code compiles with 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 flux, and the compilers are evolving to try to keep up with the standard, and so such code is in practice not very portable.

链接地址: http://www.djcxy.com/p/34952.html

上一篇: Cocoapods页面重定向到github

下一篇: Constexpr转换为const char []