Могу ли я шаблонировать определяемые пользователем литералы?

Предположим, у меня есть класс:

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

и я предоставляю некоторые определяемые пользователем литералы, которые создают объект Foo:

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.

У меня такой вопрос: почему я не могу их шаблонировать и не переписывать код?

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) и 7.0.0 (самостоятельно) сообщает:

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

Сообщение об ошибке кажется достаточно ясным, но я не вижу причин, по которым мне не следует позволять это делать в принципе; Итак, я делаю это неправильно, или это действительно запрещено?


person Zorawar    schedule 06.01.2017    source источник
comment
2.14.8/3 предполагает, что литералы определяемых пользователем операторов шаблона допустимы. Per /5, ваш код кажется мне правильным. Ваш компилятор не улавливает специализацию шаблона оператора как (в частности) определяемый пользователем литеральный оператор string, и мой шаблон fu недостаточно хорош, чтобы выяснить, является ли это ошибкой, или следствие правил поиска.   -  person Lightness Races in Orbit    schedule 06.01.2017
comment
@LightnessRacesinOrbit Думаю, [over.literal] здесь больше подходит. Кажется, что шаблон функции, который включает литеральный идентификатор оператора, должен соответствовать фиксированному объявлению (более или менее template <char...> double operator "" _x()). По-видимому, никакая другая форма не допускается.   -  person skypjack    schedule 06.01.2017
comment
@skypjack: формулировка заставляет меня предложить попробовать ее с явным созданием экземпляров.   -  person Lightness Races in Orbit    schedule 06.01.2017
comment
@LightnessRacesinOrbit: я думал то же самое о своем компиляторе (я думаю!). В любом случае, попытка указать явное создание экземпляра дала ошибку: error: template-id 'operator""_foo<>' for 'Foo<char> operator""_foo(const char*, std::size_t)' does not match any template declaration. Что, опять же, оставляет меня с вопросом: это мой компилятор не улавливает объявление моего шаблона или это просто не разрешено? Я предполагаю, что это должно быть последнее, хотя я не понимаю, почему...   -  person Zorawar    schedule 06.01.2017
comment
@Zorawar: ответ ниже очень хорош.   -  person Lightness Races in Orbit    schedule 06.01.2017
comment
@LightnessRacesinOrbit: Я знаю :) Я имел в виду следующее: я не понимаю, почему в принципе... Может быть, просто недостаточно полезно рассматривать возможность добавления, когда вы уже ограничиваете аргументы?   -  person Zorawar    schedule 06.01.2017
comment
@Зоравар: Может быть. Держу пари, просто никто не рассматривал это.   -  person Lightness Races in Orbit    schedule 06.01.2017
comment
@Zorawar Почему в принципе вам следует спросить рабочую группу или аналогичную. ;-)   -  person skypjack    schedule 06.01.2017


Ответы (1)


Рассмотрим это:

Если литеральный оператор является шаблоном, он должен иметь пустой список параметров и может иметь только один параметр шаблона, который должен быть пакетом параметров нетипового шаблона с элементом типа char.

Другими словами, объявление шаблона буквального оператора должно быть:

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

Это не ваш случай.


Я не языковой юрист, но я думаю, что раздел стандарта, который имеет отношение к вашему делу, - это [over.literal] (ссылка на рабочий проект).

Далее следует отрывок из [over.literal]/2:

Шаблон функции, объявленный с идентификатором литерала-оператора, является шаблоном литерала-оператора.

Ниже цитируется [over.literal]/5:

Объявление шаблона буквального оператора должно иметь пустое предложение-объявления-параметра, а его список-параметров-шаблона должен иметь один параметр-шаблона, который представляет собой нетиповой пакет параметров шаблона ([temp.variadic]) с типом элемента char .

Мне кажется, что объявления, подобные заявленному в вопросе, явно запрещены стандартом.
В общем, шаблон функции, который объявляет литеральный оператор, должен строго соответствовать заданному шаблону.


я делаю это неправильно, или это действительно не разрешено?

Я бы сказал, что это действительно запрещено.

В любом случае, вы все равно можете использовать шаблонную функцию, если у вас есть сложная логика, которую вы не хотите повторять в каждом операторе:

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);
}

Это вопрос дополнительного уровня косвенности, вот и все.
Очевидно, что это того не стоит, если все ваши операторы представляют собой однострочные функции тела.

person skypjack    schedule 06.01.2017
comment
Думаю, это достаточно ясно. Интересно, почему они не позволили это, однако. Возможно, будущие стандарты ослабят этот запрет... - person Zorawar; 06.01.2017
comment
@Zorawar Мне просто любопытно, как и тебе, но я не могу понять, почему это запрещено. Мне жаль. - person skypjack; 06.01.2017
comment
Это нормально! Спасибо за ответ. По крайней мере, я знаю, что не только что совершил глупую ошибку! - person Zorawar; 06.01.2017