如何在 C++17 中定义用户定义的字符串文字运算符模板?

bit*_*ask 5 c++ g++ string-literals clang++ c++17

根据我的C++17(草案)版本(16.5.8 [over.literal]) 以及cppreference.com,C++17应该支持用户定义的字符串文字的模板化运算符。

具体来说:

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

int main() {
  "test"_pi;
}
Run Code Online (Sandbox Code Playgroud)

然而,gcc 和 clang 都对我大喊大叫:

// 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
Run Code Online (Sandbox Code Playgroud)

现场演示

他们似乎都想推动我使用以下模板,该模板曾经被简单地认为是 C++17 的一部分,但据我了解并没有成为其中的一部分:

template <typename T, T...>
double operator "" _pi() {
    return 0.;
}
Run Code Online (Sandbox Code Playgroud)

然而,gcc 和 clang 都正确地警告这是一个非标准扩展,但它们正确地编译了它。gcc和clang声称完全支持 C++17 核心语言。

我究竟做错了什么?我的草稿cppreference.com 是否不准确?我目前无法访问最终标准版本。


使用案例

我的用例是实现以下内容,这就是我需要编译时信息的原因。

template <char... cs>
constexpr auto operator "" _a() {
  return std::array<char, sizeof...(cs)>({cs...});
}
// "Hello"_a creates a std::array<char,5>
Run Code Online (Sandbox Code Playgroud)

Nat*_*ica 4

模板char...用户定义的文字不适用于字符串。它称为数字文字运算符模板,它允许您拥有一个文字运算符,该运算符将获取您正在使用的数字并将其扩展为模板运算符的包。例如,您可以使用像这样的运算符

1234_pi
Run Code Online (Sandbox Code Playgroud)

这将解决调用

operator "" <'1', '2', '3', '4'> _pi()
Run Code Online (Sandbox Code Playgroud)

你想要的东西在库存中是不可能的 C++17

要使其正常工作,您需要升级到 C++20,它允许您使用可以从字符串文字构造的用户定义类型,并使用它来构建std::array. 这就是现在所说的字符串文字运算符模板。那看起来像

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;
}
Run Code Online (Sandbox Code Playgroud)

进而

"test"_foo;
Run Code Online (Sandbox Code Playgroud)

这将解决一个std::array<char, 5>. 如果您不希望数组中出现空终止符,则只需1从除部分N之外的所有使用它的地方减去即可char const(&arr)[N]

如果你不能使用 C++20,你可以将其切换到几个函数,例如

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>());
}
Run Code Online (Sandbox Code Playgroud)

然后你会像这样使用它

as_array("test")    
Run Code Online (Sandbox Code Playgroud)