Ответ 1
Рассмотрим этот код:
template<class T> using identity = T;
template<class T> void foo(identity<T>&&) { } //#1
template<class T> void foo(T&&) { } //#2
int main()
{
int i{};
foo(i);
}
Оба GCC и Clang отклоняют его, потому что #2
является переопределением #1
. Если они фактически являются одним и тем же шаблоном, мы могли бы ожидать, что #1
будет вести себя точно так же, как #2
, а это означает, что identity<T>&&
должен действовать как ссылка для пересылки. Следуя этой логике, мы не знаем, какой из них прав, но GCC по крайней мере непротиворечивый.
Это также согласуется с очень похожим примером в стандарте в [14.5.7p2].
Мы также должны рассмотреть способ работы с аргументом шаблона аргумента. Если identity
был шаблоном класса, его форма могла быть сопоставлена с типом аргумента функции, не глядя на его определение, позволяя компилятору вывести аргумент шаблона для T
. Однако здесь мы имеем шаблон псевдонима; T
не может быть выведено на int
или int&
или что-либо еще, если identity<T>
не заменяется на T
. В противном случае, с чем мы согласны? После того, как замена выполнена, параметр функции становится ссылкой пересылки.
Все вышеперечисленное поддерживает идею identity<T>&&
(и identity<T&&>
), которая рассматривается как эквивалентная справочной системе пересылки.
Однако, похоже, что к этому добавляется немедленная замена идентификатора шаблона псевдонима соответствующим идентификатором типа. В параграфе [14.5.7p3] говорится:
Однако, если идентификатор шаблона зависит, последующий аргумент шаблона подстановка по-прежнему применяется к идентификатору шаблона. [Пример:
template<typename...> using void_t = void; template<typename T> void_t<typename T::foo> f(); f<int>(); // error, int does not have a nested type foo
-end пример]
Возможно, это не имеет большого отношения к вашему примеру, но на самом деле это указывает на то, что исходная форма идентификатора шаблона все же учитывается в некоторых случаях независимо от замещенного идентификатора типа. Я предполагаю, что это открывает возможность того, что identity<T>&&
фактически не может считаться ссылкой для пересылки.
Эта область, по-видимому, указана в стандарте ниже. Это показывает количество открытых вопросов, связанных с аналогичными проблемами, по-моему, в одной и той же категории: в каких случаях первоначальная форма идентификатора шаблона учитывается при создании экземпляра, хотя предполагается, что он должен быть заменен соответствующий идентификатор типа сразу после его возникновения. См. Вопросы 1980, 2021 и 2025. Даже проблемы 1430 и 1554 могут рассматриваются как имеющие отношение к аналогичным проблемам.
В частности, issue 1980 содержит следующий пример:
template<typename T, typename U> using X = T;
template<typename T> X<void, typename T::type> f();
template<typename T> X<void, typename T::other> f();
с примечанием:
CWG считает, что эти два объявления не должны быть эквивалентными.
(CWG - рабочая группа Core)
Аналогичная линия рассуждений может применяться к вашему примеру, делая identity<T>&&
не эквивалентным ссылке пересылки. Это может даже иметь практическое значение, поскольку простой способ избежать жадности ссылки на пересылку, когда все, что вам нужно, - это ссылка rvalue на выведенный T.
Итак, я думаю, вы подняли очень интересную проблему. В качестве примера можно привести примечание к issue 1980, чтобы убедиться, что это учитывается при разработке разрешения.
На мой взгляд, ответ на ваш вопрос на данный момент звучит "кто знает?".
Обновление: в комментариях к другому, связанному, вопросу, Piotr S. указал issue 1700, который был закрыт как "не дефект". Это относится к очень похожему случаю, описанному в этом вопросе, и содержит следующее обоснование:
Поскольку типы параметров функции одинаковы, независимо от того, написаны ли они напрямую или через шаблон псевдонима, в обоих случаях вывод должен выполняться одинаково.
Я думаю, что это применимо также к рассмотренным здесь случаям и решает проблему на данный момент: все эти формы следует рассматривать как эквивалентные справочной информации пересылки.
(Будет интересно посмотреть, косвенно ли это косвенное изменение в разрешениях для других открытых проблем, но в основном они имеют дело с ошибками замены, а не с дедукцией, поэтому я предполагаю, что такой косвенный эффект маловероятен.)
Все стандартные ссылки относятся к текущему рабочему чертежу, N4431, второму проекту после окончательного С++ 14.
Обратите внимание, что цитата из [14.5.7p3] является недавним добавлением, включенным сразу после окончательной версии С++ 14 в качестве разрешения DR1558. Я думаю, мы можем ожидать дальнейших дополнений в этой области, так как другие проблемы решаются так или иначе.
До тех пор, возможно, стоит задать этот вопрос в группе ISO С++ Standard - Discussion; что должно привлечь внимание соответствующих людей.