Can I template user-defined literals?

4k views Asked by At

Suppose I have some class:

template <typename T>
class Foo {
  const T* x_;
public:
  Foo(const T* str) : x_{str} {}
};

and I provide some user-defined literals that create a Foo object:

Foo<char> operator"" _foo(const char* str, std::size_t) {
  return Foo<char>{str};
}

Foo<wchar_t> operator"" _foo(const wchar_t* str, std::size_t) {
  return Foo<wchar_t>{str};
}

// etc. for char16_t and char32_t.

My question is this: why can I not template these and save having to rewrite code?

template <typename T>
Foo<T> operator"" _foo(const T* str, std::size_t) {
  return Foo<T>{str};
}

gcc 5.4.0 (Ubuntu 5.4.0-6ubuntu1~16.04.4) and 7.0.0 (compiled myself) report:

error: ‘Foo<T> operator""_foo(const T*, std::size_t)’ has invalid argument list
Foo<T> operator"" _foo(const T* str, std::size_t) {
                                                ^

The error message seems to be clear enough, but I don't see a reason why I shouldn't be allowed to do this in principle; so, am I doing this incorrectly, or is this genuinely not allowed?

1

There are 1 answers

3
skypjack On BEST ANSWER

Consider this:

If the literal operator is a template, it must have an empty parameter list and can have only one template parameter, which must be a non-type template parameter pack with element type char

In other terms, the declaration of a literal operator template should be:

template <char...> double operator "" _x();

That is not your case.


I'm not a language-lawyer, but I guess the section of the standard that is relevant for your case is [over.literal] (link to the working draft).

An excerpt from [over.literal]/2 follows:

A function template declared with a literal-operator-id is a literal operator template.

Below [over.literal]/5 is quoted:

The declaration of a literal operator template shall have an empty parameter-declaration-clause and its template-parameter-list shall have a single template-parameter that is a non-type template parameter pack ([temp.variadic]) with element type char.

It seems to me that declarations similar to the one in the question are explicitly disallowed by the standard.
More in general, a function template that declares a literal operator must adhere strictly to the given pattern.


am I doing this incorrectly, or is this genuinely not allowed?

I would say that's genuinely not allowed.

Anyway, you can still use a template function if you have complex logic that you don't want to repeat in each operator:

template<typename T>
Foo<T> create(const T *str) {
    // your logic...
    return Foo<T>{str};
}

Foo<char> operator"" _foo(const char *str, std::size_t) {
    return create(str);
}

Foo<wchar_t> operator"" _foo(const wchar_t *str, std::size_t) {
    return create(str);
}

It's a matter of an extra layer of indirection and that's all.
Obviously, it doesn't worth it if all your operators are one line body functions.