Взглянув на предлагаемое разрешение # 1664 ( предлагаемое разрешение 1664 ), я запутался в правилах аргумента по умолчанию для шаблона функции. Цитируйте содержимое здесь:
В соответствии с пунктом 8.1.5 [expr.prim.lambda] 3
Тип замыкания объявляется в наименьшей области блока, области класса или области пространства имен, которая содержит соответствующее лямбда-выражение. [Примечание: это определяет набор пространств имен и классов, связанных с типом замыкания (6.4.2 [basi c .lookup.argdep]). Типы параметров лямбда-декларатора не влияют на эти связанные пространства имен и классы. - конец примечания]
Тем не менее, 17.8.1 [temp.inst] параграф 13 говорит:
Если шаблон функции f вызывается способом, который требует аргумент по умолчанию для использования, ищутся зависимые имена, проверяются ограничения семантики, и создается экземпляр любого шаблона, используемого в аргументе по умолчанию, как если бы аргумент по умолчанию был инициализатором, используемым в специализации шаблона функции с той же областью действия, те же параметры шаблона и тот же доступ, что и у шаблона функции f, используемого в этой точке.
Таким образом, существует возможность того, что тип замыкания для лямбда-выражения в аргументе по умолчанию для функции шаблона (или, предположительно, функции-члена шаблона класса) следует рассматривать как объявляется в некоторой области видимости в теле вымышленной функции шаблона специализации.
Рассмотрим следующий пример:
namespace J {
inline namespace K {
template <typename T> int zap(const T &t) { foo(t); return 0; }
template <typename T> void zip(int = zap([] { })) { }
}
template <typename T> void foo(const T &) { }
}
void bar() {
J::K::zip<long>();
/*Accroding to the above wording,the invoke just like:
=> J::K::zip<long>(zap([] { }));
*/
}
Если zip не был шаблоном, зависимый от аргумента поиск успешно разрешает поиск для foo во всех протестированных реализациях; тем не менее, существует разница в реализации в примере, как написано.
Предлагаемое решение (сентябрь 2013 г.):
Изменить 17.8.1 [temp.inst] параграф 13 следующим образом:
Если шаблон функции f вызывается в способ, который требует использования аргумента по умолчанию, поиск зависимых имен, проверка семантических ограничений и создание экземпляра любого шаблона, используемого в аргументе по умолчанию, выполняется так, как если бы аргумент по умолчанию был инициализатором, используемым в функции специализация шаблона с той же областью действия, теми же параметрами шаблона и тем же доступом, что и у шаблона функции f, используемого в этой точке, , за исключением той области действия, в которой объявлен тип замыкания (8.1.5 [expr.prim. лямбда]) - и, следовательно, связанные с ним пространства имен - остаются такими, как определено в контексте определения аргумента по умолчанию . Этот анализ называется созданием аргумента по умолчанию. Созданный по умолчанию аргумент по умолчанию затем используется как аргумент функции f
Обратите внимание на выделенную часть, если я не понимаю неправильно, это означает, что если выделенная часть закомментирована, foo
не может быть найден с помощью зависимого от аргумента поиска вверх , потому что аргумент [] { }
, пространство имен которого не является ни J
, ни K
, принимает форму в пределах function bar
подобно J::K::zip<long>(zap([] { }) /*default argument*/);
, таким образом, согласно [expr.prim.lambda] параграф 3 пространство имен [] { }
находится в fuction bar
, и в этой области не может быть найдено foo
, поэтому выделенная часть предназначена для этого случая, в котором пространство имен [] { }
в zap
совпадает с zap
, это означает, что пространство имен [] { }
равно K
, теперь foo
можно найти в родительском пространстве имен J
по правилам поиска, зависящим от аргумента. Пока, если я неправильно понимаю эти правила, исправьте меня. Другой момент точка зрения заключается в том, что аргумент по умолчанию оценивается каждый раз, когда вызывается функция, даже если по умолчанию не зависит . Поэтому продолжайте рассматривать следующий код:
#include <iostream>
struct A {
};
template<typename T>
int func(T, float) { //#a
std::cout << "float" << std::endl;
return 0;
}
template<typename T>
void test(int = func(A{}, 0)) { //#1
}
template<typename T>
int func(T, int) { //#b
std::cout << "int" << std::endl;
return 0;
}
int main() {
test<A>(); //#2 transform to: test<A>(func(A{}, 0)); here,#b should be the best match
std::getchar();
}
Хотя аргумент по умолчанию * 1 060 * не зависит, однако его следует определять каждый раз, когда вызывается функция test
, и я проверяю код в некоторых компиляторах.
Все версии MSV C отчет "int", g cc отчет "float", clang отчет "float", какого черта? Согласно отчету g cc или clang, кажется, func
определяется при #1
, а MSV C доказывает, что func
определяется при #2
. если MSV C неверно, это означает, что независимый аргумент по умолчанию может быть определен в # 1, и нет необходимости определять каждый раз, когда вызывается функция, зачем выделенную часть добавлять? ( Если я понимаю Правильно подчеркнутая часть, цель этого состоит в том, чтобы поддерживать постоянство пространства имен типа замыкания в аргументе по умолчанию независимо от того, находится ли лямбда-выражение в точке объявления функции или в точке вызова ). Если я неправильно понимаю эти правила, как их правильно интерпретировать?
ОБНОВЛЕНИЕ:
версия 9.1 или выше g cc не может скомпилировать ie код, упомянутый в # 1664, это сообщит об ошибке ( compl ie result )
Вопросы:
1. Нужен ли независимый аргумент по умолчанию для шаблона функции или функции без шаблона определяется каждый раз, когда вызывается соответствующая функция?
2.Что означает «определение аргумента по умолчанию»? Строго ли существует эта формулировка? ( Другими словами, я понимаю, что actullay добавленные правила хотят, чтобы express заключалось в том, что Пространство имен типа закрытия является пространством имен функции, в котором содержится аргумент по умолчанию, содержащий соответствующее лямбда-выражение, верно? если мое понимание этого неверно, исправьте меня )