10_x; // empty template list, how to specify non-empty template parameters?
That isn't quite right. The template parameter list isn't empty. When you write:
template <char... Cs>
??? operator "" _x()
The Cs
get populated from the stuff on the left-hand side of the literal. That is, when you write:
10_x
that calls:
operator ""_x<'1', '0'>();
One simple example would be to build a compile time, overflow-safe binary literal such that:
template <uint64_t V>
constexpr uint64_t make_binary() {
return V;
}
template <uint64_t V, char C, char... Cs>
constexpr uint64_t make_binary() {
static_assert(C == '0' || C == '1', "invalid binary");
return make_binary<2*V + C - '0', Cs...>();
}
template <char... Cs>
uint64_t operator "" _b()
{
static_assert(sizeof...(Cs) <= 64, "overflow");
return make_binary<0, Cs...>();
}
uint64_t a = 101_b; // OK: a == 5
uint64_t b = 102_b; // error: invalid
uint64_t c = 11111111110000000000111111111100000000001111111111000000000011111111110000000000_b; // error: overflow
The template char...
user defined literal is not for strings. It is called a numeric literal operator template and it allows you to have a literal operator that will take the number you are using and expand it into a pack for the template operator. For example, you would use your operator like
1234_pi
and that would resolve to calling
operator "" <'1', '2', '3', '4'> _pi()
What you'd like to have is not possible in stock C++17
To get this to work, you'll need to upgrade to C++20, which lets you use a user defined type that can be constructed from a string literal, and use that to build the std::array
. This is what is now called a string literal operator template.
That would look like
template<std::size_t N>
struct MakeArray
{
std::array<char, N> data;
template <std::size_t... Is>
constexpr MakeArray(const char (&arr)[N], std::integer_sequence<std::size_t, Is...>) : data{arr[Is]...} {}
constexpr MakeArray(char const(&arr)[N]) : MakeArray(arr, std::make_integer_sequence<std::size_t, N>())
{}
};
template<MakeArray A>
constexpr auto operator"" _foo()
{
return A.data;
}
and then
"test"_foo;
and that will resolve to a std::array<char, 5>
. If you don't want the null terminator in the array, You just need to subtract 1
from N
everywhere it is used except for the char const(&arr)[N]
parts.
If you can't use C++20, you could switch this over to a couple of functions like
template <std::size_t N, std::size_t... Is>
constexpr auto as_array_helper(const char (&arr)[N], std::integer_sequence<std::size_t, Is...>)
{
return std::array<char, N>{arr[Is]...};
}
template <std::size_t N>
constexpr auto as_array(const char (&arr)[N])
{
return as_array_helper(arr, std::make_integer_sequence<std::size_t, N>());
}
and then you would use it like
as_array("test")
Best Answer
There where complications involving character sets and encoding and other such mess. What happens if the character set encoding differs between runtime and compile time, what happens if there are multiple ways to encode the same character, does the string being utf-8 encoded vs a different encoding do anything? Do you translate to the runtime character set, or not?
Maybe there are easy answers; but there was enough complication that the proposers went "ok, we'll cut this" and the literals got into the standard.
The characters permitted for floating point and integral literals where limited, and did not have this problem.
But you linked to a document that linked to N2750, and which directly stated: