4

According to my (draft) version of C++17 (16.5.8 [over.literal]) as well as as cppreference.com, C++17 should have support for templated operators for user-defined string literals.

Specifically:

template <char...>
double operator "" _pi() {
    return 0.;
}

int main() {
  "test"_pi;
}

However, both gcc and clang yell at me:

// gcc -Wall -Wextra -pedantic -std=c++17
error: no matching function for call to 'operator""_pi<char, 't', 'e', 's', 't'>()'
    7 |   "test"_pi;
      |   ^~~~~~~~~
note: candidate: 'template<char ...<anonymous> > double operator""_pi()'
    2 | double operator "" _pi() {

// clang -Wall -Wextra -pedantic -std=c++17
error: no matching literal operator for call to 'operator""_pi' with arguments of types 'const char *' and 'unsigned long', and no matching literal operator template

(live demo)

They both seem to want to nudge me towards the following template, which used be briefly considered to be part of C++17, but to my understanding did not become part of it:

template <typename T, T...>
double operator "" _pi() {
    return 0.;
}

However, both gcc and clang correctly warn that this is a non-standard extension but they compile it correctly. Both gcc and clang claim to have full support of the C++17 core language.

What am I doing wrong? Is my draft AND cppreference.com inaccurate? I currently don't have access to the final standard version.


Use case

My use case is to implement the following, which is why I need the compile time information.

template <char... cs>
constexpr auto operator "" _a() {
  return std::array<char, sizeof...(cs)>({cs...});
}
// "Hello"_a creates a std::array<char,5>
bitmask
  • 32,434
  • 14
  • 99
  • 159
  • See [Is it possible to create templated user-defined literals (literal suffixes) for string literals?](https://stackoverflow.com/questions/40920149/is-it-possible-to-create-templated-user-defined-literals-literal-suffixes-for) – cpplearner Apr 09 '21 at 13:14
  • @cpplearner Interesting. But that seems to be a pre-C++17 answer, where, indeed, this wasn't part of the standard. – bitmask Apr 09 '21 at 13:16

1 Answers1

4

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")    
cigien
  • 57,834
  • 11
  • 73
  • 112
NathanOliver
  • 171,901
  • 28
  • 288
  • 402
  • 1
    But, even in C++20, we cannot have an UDL which returns a char sequence :-/ (to pass compile time string) – Jarod42 Apr 09 '21 at 14:19
  • That explains why I was allowed to define the operator, but not use it as intended. Good explanation of my misconception +1 – bitmask Apr 09 '21 at 14:35
  • Does anybody know if UDLs returning char sequences will ever become a standard C++ feature? For example in C++23? GCC and Clang already support it, so it can definitely be done and I see no reason why it should not become "standard" given its usefulness! – fdev Nov 01 '22 at 10:33