Как размещение определения понятия меняет поведение программы - PullRequest
3 голосов
/ 03 апреля 2020

Я компилирую этот код с gcc 9.3 с -fconcepts.

Следующие компиляции успешно

void f(int) {}             // 1

template<typename T>       // 2 
concept C = requires (T a) 
{ { f(a) }; };

template<C T>              // 3
void g() { f(42); }

int main() { g<int>(); }   // 4

Однако, если я определю функцию f после я определяю концепцию C,

template<typename T>       // 2 
concept C = requires (T a) 
{ { f(a) }; };

void f(int) {}             // 1

template<C T>              // 3
void g() { f(42); }

int main() { g<int>(); }   // 4

, затем программа не может скомпилироваться с

error:
line 4: cannot call function g
because
line 3: constraint not satisfied
because
line 2: required expression f(a) would be ill-formed

Это кажется странным, поскольку к тому времени g<int> необходимо быть созданным, определение f должно быть видимым. Может ли кто-нибудь объяснить, что здесь происходит?

Обратите внимание, что если я объявлю f перед определением концепции, то даже если я определю f впоследствии, программа успешно скомпилируется.

Ответы [ 2 ]

4 голосов
/ 03 апреля 2020

ADL не относится к int, поэтому f(a)int a) плохо сформирован, если не объявлено ранее.

У вас возникнут аналогичные проблемы с:

void f(int){}

template<typename T>
void g(T t)
{
    f(t);
}
void f(char){}

void h() { g('*'); } // Call f(int)
0 голосов
/ 04 апреля 2020

Это основано на ответе @ Jarod42 .

В представленном примере аргумент f является фундаментальным типом, поэтому согласно это правило ADL не рассматривает определение f, которое следует после определения C, и пример, который не компилируется, является некорректным (поскольку нет никаких объявлений, предшествующих его использованию).

Однако , если аргумент f имеет тип класса, как в следующем фрагменте,

template<typename T> 
concept C = requires (T a) { { f(a) }; };

struct S {};

void f(S) {}

template<C T>
void g() { f(S{}); }

int main() { g<S>(); }   

, тогда это правило гласит, что рассматриваются все связанные объекты в окружающем пространстве имен. Это делает приведенный выше фрагмент правильно сформированным и успешно компилируется, даже если f не объявлено до определения C.

...