Позиция модификатора Declarator в вариативных шаблонах
Declarators. Да, деклараторы. Они являются источником множества дискуссий по кодированию. Это действительно хорошая тема для аргументов - С++ не определяет, какой из них лучше другого (это неважно!). И поэтому мы можем написать их, не беспокоясь о том, что кто-то может высмеять вас:
int x;
int& a = x;
int &b = x;
int* c = &x;
int *d = &x;
В синтаксических терминах b
и d
являются "более достоверными", чем остальные. Мы должны указать модификаторы декларатора перед именами:
int m, *n; // m is an int; n is a pointer to int
Но поток, похоже, обратился в пользу одного. С помощью вариативных шаблонов С++ 11 положение деклараторов, по-видимому, ограничено формой, в которой модификаторы ближе к базовому типу:
template<typename... Ts>
void VariadicFuncRef(const Ts&... args) { }
^
template<typename... Ts>
void VariadicFuncPtr(Ts*... args) { }
^
Ошибка записи этих форм:
template<typename... Ts>
void VariadicFuncRef(const Ts... &args) { }
template<typename... Ts>
void VariadicFuncPtr(Ts... *args) { }
Для конкретного примера нажмите здесь.
Итак, мой вопрос таков:
Почему мы ограничиваемся этой (юридической) формой и не можем использовать другую?
Любые мысли ребята?
ДОПОЛНИТЕЛЬНЫЙ 1: Меня также интересует дизайнерское решение о том, почему это "правило" "принудительно".
Ответы
Ответ 1
Прежде всего, я бы не сказал, что int &a
более допустим, чем int& a
. Вы можете сказать, что это более читаемо или хорошая практика, но это совсем другое.
Во-вторых, синтаксис распаковки параметров T...
можно читать как "расширяется тип-шаблон в левой части ...
, образуя одинаковые или разные типы параметров фактической функции, соответствующие форме шаблона типа". Поэтому, когда вы пишете f(T&...)
, его можно развернуть до f(int&, float&, double&, Xyz&, Abc&)
. Но это не кажется настолько очевидным (по крайней мере для меня), когда синтаксис T...&
. Так что, возможно, это одна из нескольких других возможных причин, почему стандарт сделал это так.
Также обратите внимание, что args
в T&...args
является необязательным. Поэтому, если вы его не пишете, то void f(T...&)
выглядит странно (по крайней мере, для меня) и void f(T&...)
выглядит эстетически по крайней мере.
Вы также можете сравнить синтаксисы, такие как: T * const & ...
с T... * const &
. Тип-образец более очевиден в предыдущем синтаксисе, чем в последнем.
Ответ 2
Старый синтаксис объявления был просто одним правилом с незначительным пробелом, но ваша идея - две. Я сомневаюсь, что комитет будет поддерживать два правила, если их будет достаточно.
Выбор размещения декларатора слева был, скорее всего, сделан по двум причинам:
- idiomatic С++ в настоящее время предпочитает
int* x
, а не int *x
(например, Bjarne использует первый). Большинство считают синтаксис декларатора ошибкой.
- Расширение пакета параметров расширяет элементы слева от
...
, а не направо.
Ответ 3
Совершенство достигается, когда нечего добавить, но когда ничего не осталось, чтобы отнять.
- Антуан де Сент-Экзюпери
Трудность программирования языка программирования заключается не в добавлении функций, а в воздержании от их добавления. Каждая функция, каждая вариация синтаксиса, имеет стоимость:
- они должны быть специально закодированы в компиляторе
- они создают риск появления ошибок
- они создают риск плохо взаимодействовать с другим признаком/изменением
- ...
Синтаксис в C и С++ является (в основном) пробелом без учета чувствительности, поэтому не имеет значения, где вы размещаете &
или *
, и обе предпочтения могут сосуществовать без дополнительной нагрузки; однако в случае вариационных шаблонов это имеет значение из-за токена ...
. Таким образом, комитет решил выбрать один, и они выбрали самый идиоматический в С++ (который делал упор на полный тип, а не на базовый тип).