Как понять предлагаемое постановление № 1664 - PullRequest
8 голосов
/ 15 марта 2020

Взглянув на предлагаемое разрешение # 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 заключалось в том, что Пространство имен типа закрытия является пространством имен функции, в котором содержится аргумент по умолчанию, содержащий соответствующее лямбда-выражение, верно? если мое понимание этого неверно, исправьте меня )

1 Ответ

3 голосов
/ 16 марта 2020

Аргументы по умолчанию оцениваются каждый раз, когда они вызываются, но это свойство времени выполнения: вызовы учитываются не по строке источника, а по фактическому потоку управления. Отдельно, аргумент по умолчанию для шаблонной функции считается определением и создается при необходимости, до одного раза за специализацию функции (с обычная оговорка о нескольких точках реализации, с которыми нужно согласиться). CWG1664 был очень узким вопросом, основанным на том, как этот экземпляр был сформулирован : введя шаблон фиктивной функции, он оставил открытой возможность того, что объявление лямбды «физически» перемещено. Исправление действительно влияет только на ADL.

Ваш пример func вместо этого иллюстрирует обычные правила поиска имен в шаблонах: независимо от того, сколько раз и аргумент откуда test по умолчанию создается, func в нем не является зависимым именем и, таким образом, находит func(T,float) (каждый раз). MSV C, как известно, никогда не реализовывали это правило правильно (потому что, честно говоря, их реализация предшествует правилу, и они только недавно начали необходимое (и почти полное) переписывание поддержки своих шаблонов).

Между тем, недавний G CC явно глючит с примером CWG1664: обратите внимание, что он жалуется на то, что foo используется, но не определен, противоречит и явно видимым { } и его предыдущее сообщение об ошибке не найдено.

...