#c #gcc #c 20 #c -concepts
#c #gcc #c 20 #c -концепции
Вопрос:
Я экспериментирую с концепциями C 20, используя собственную локальную сборку клона исходного кода GCC 11. Я получаю ошибки компиляции из GCC, которые кажутся мне неправильными. Я сократил свой код, запускающий диагностику, до того, что приведено ниже, с помощью командной строки GCC для его компиляции и полного результирующего диагностического вывода ниже.
Для фокуса, конкретный раздел диагностического вывода, который мне кажется неправильным, это
repro.cpp:9:13: note: the required expression ‘flequal(a, b)’ is invalid, because
9 | {flequal(a, b)} -> std::convertible_to<bool>;
| ~~~~~~~^~~~~~
repro.cpp:9:13: error: ‘flequal’ was not declared in this scope, and no declarations were found by argument-dependent lookup at the point of instantiation [-fpermissive]
repro.cpp:22:6: note: ‘template<class auto:1, class auto:2> requires (Flequalable<auto:1>) amp;amp; (Flequalable<auto:2>) bool flequal(const Bar<auto:1>amp;, const Bar<auto:2>amp;)’ declared here, later in the translation unit
22 | bool flequal(Bar<Flequalable auto> const amp;a, Bar<Flequalable auto> const amp;b) {
| ^~~~~~~
В нем говорится, что функция-кандидат была объявлена позже в блоке перевода, чем точка создания экземпляра, но это неверно, по крайней мере, в соответствии с порядком строк исходного кода в моем фрагменте кода.
Это ошибка сборки или я делаю что-то не так при использовании концепций C 20?
Обратите внимание на вариант в комментарии в конце кода, который компилируется без какой-либо диагностики.
Код:
#include <concepts>
using std::floating_point;
template< typename T >
concept Flequalable
= floating_point<T>
amp;amp; requires(T a, T b) {
{flequal(a, b)} -> std::convertible_to<bool>;
};
template<floating_point T>
bool flequal( T a, T b) {
return true;
}
template<typename T>
struct Bar {
T t;
};
bool flequal(Bar<Flequalable auto> const amp;a, Bar<Flequalable auto> const amp;b) {
return true;
}
bool foo() {
Bar<double> a = {2.0};
Bar<double> b = {3.0};
return flequal(a, b); // Causes diagnostic
// return flequal(a.t, b.t); // This works
}
Командная строка компиляции:
/usr/local/gcc-11-master/bin/g -11 -c repro.cpp -o repro.o -std=c 20 -fconcepts-diagnostics-depth=5 2> diagnostic.txt
Полная диагностика GCC:
repro.cpp: In function ‘bool foo()’:
repro.cpp:29:24: error: no matching function for call to ‘flequal(Bar<double>amp;, Bar<double>amp;)’
29 | return flequal(a, b); // Causes diagnostic
| ^
repro.cpp:13:6: note: candidate: ‘template<class T> requires floating_point<T> bool flequal(T, T)’
13 | bool flequal( T a, T b) {
| ^~~~~~~
repro.cpp:13:6: note: template argument deduction/substitution failed:
repro.cpp:13:6: note: constraints not satisfied
In file included from repro.cpp:1:
/usr/local/gcc-11-master/include/c /11.0.1/concepts: In substitution of ‘template<class T> requires floating_point<T> bool flequal(T, T) [with T = Bar<double>]’:
repro.cpp:29:24: required from here
/usr/local/gcc-11-master/include/c /11.0.1/concepts:111:13: required for the satisfaction of ‘floating_point<T>’ [with T = Bar<double>]
/usr/local/gcc-11-master/include/c /11.0.1/concepts:111:30: note: the expression ‘is_floating_point_v<_Tp> [with _Tp = Bar<double>]’ evaluated to ‘false’
111 | concept floating_point = is_floating_point_v<_Tp>;
| ^~~~~~~~~~~~~~~~~~~~~~~~
repro.cpp:22:6: note: candidate: ‘template<class auto:1, class auto:2> requires (Flequalable<auto:1>) amp;amp; (Flequalable<auto:2>) bool flequal(const Bar<auto:1>amp;, const Bar<auto:2>amp;)’
22 | bool flequal(Bar<Flequalable auto> const amp;a, Bar<Flequalable auto> const amp;b) {
| ^~~~~~~
repro.cpp:22:6: note: template argument deduction/substitution failed:
repro.cpp:22:6: note: constraints not satisfied
repro.cpp: In substitution of ‘template<class auto:1, class auto:2> requires (Flequalable<auto:1>) amp;amp; (Flequalable<auto:2>) bool flequal(const Bar<auto:1>amp;, const Bar<auto:2>amp;) [with auto:1 = double; auto:2 = double]’:
repro.cpp:29:24: required from here
repro.cpp:6:9: required for the satisfaction of ‘Flequalable<auto:1>’ [with auto:1 = double]
repro.cpp:8:8: in requirements with ‘T a’, ‘T b’ [with T = double]
repro.cpp:9:13: note: the required expression ‘flequal(a, b)’ is invalid, because
9 | {flequal(a, b)} -> std::convertible_to<bool>;
| ~~~~~~~^~~~~~
repro.cpp:9:13: error: ‘flequal’ was not declared in this scope, and no declarations were found by argument-dependent lookup at the point of instantiation [-fpermissive]
repro.cpp:22:6: note: ‘template<class auto:1, class auto:2> requires (Flequalable<auto:1>) amp;amp; (Flequalable<auto:2>) bool flequal(const Bar<auto:1>amp;, const Bar<auto:2>amp;)’ declared here, later in the translation unit
22 | bool flequal(Bar<Flequalable auto> const amp;a, Bar<Flequalable auto> const amp;b) {
| ^~~~~~~
Вот точная информация о версии GCC:
Using built-in specs.
COLLECT_GCC=/usr/local/gcc-11-master/bin/g -11
COLLECT_LTO_WRAPPER=/usr/local/gcc-11-master/libexec/gcc/x86_64-pc-linux-gnu/11.0.1/lto-wrapper
Target: x86_64-pc-linux-gnu
Configured with: ../gcc/configure --prefix=/usr/local/gcc-11-master --program-suffix=-11 --enable-libstdcxx-debug : (reconfigured) ../gcc/configure --prefix=/usr/local/gcc-11-master --program-suffix=-11 --enable-libstdcxx-debug
Thread model: posix
Supported LTO compression algorithms: zlib
gcc version 11.0.1 20210304 (experimental) (GCC)
Комментарии:
1. точка создания экземпляра — сложная концепция. Кажется, у вас и компилятора разные представления о том, где это в данном случае.
Ответ №1:
Во-первых, Bar<Flequalable auto>
недопустимый синтаксис. У вас может быть переменная верхнего уровня, объявленная как Concept auto v
, но вы не можете вложить ее в шаблоны. Так что это действительно должно быть:
template <Flequalable T, Flequalable U>
bool flequal(Bar<T> const amp;a, Bar<U> const amp;b) {
return true;
}
Теперь, что происходит, когда мы пытаемся вызвать flequal(a, b)
. Мы выводим T=double
, U=double
а затем пытаемся оценить double
, удовлетворяет ли Flequalable
. double
есть floating_point
, поэтому мы продолжаем проверять требование, которое flequal(a, b)
является допустимым выражением для двух double
s.
Это неквалифицированный поиск, поэтому сначала мы выполняем обычный неквалифицированный поиск имени flequal
. Важно иметь в виду, что этот поиск происходит с точки определения концепции, а не с точки, где он используется. То есть мы смотрим отсюда:
#include <concepts>
using std::floating_point;
template< typename T >
concept Flequalable
= floating_point<T>
amp;amp; requires(T a, T b) {
{flequal(a, b)} -> std::convertible_to<bool>; // <== here!
};
Не там, где используется концепция:
template <Flequalable T, Flequalable U> // <== not here
bool flequal(Bar<T> const amp;a, Bar<U> const amp;b) {
return true;
}
Ни там, где вызывается шаблон функции, использующий концепцию:
return flequal(a, b); // <== not here either
Мы ничего не находим. flequal
С этой точки нет объявлений visible . Затем мы выполняем поиск, зависящий от аргумента. double
не имеет связанных пространств имен, поэтому это также тривиально ничего не находит. Таким образом, кандидатов ноль, поэтому double
не удовлетворяет Flequalable
.
Чтобы это сработало, вы должны убедиться, что неквалифицированный поиск в концепции Flequalable
действительно находит вашу flequal
функцию. Просто поменяйте местами два объявления, и тогда все будет работать нормально.
Это создает впечатление, что flequal
это какая-то точка настройки, но существует только фиксированное количество floating_point
типов ( float
, double
, long double
, и их cv-версии), поэтому я не совсем уверен, в чем здесь смысл.
Комментарии:
1. Я думаю, это помогло бы избежать путаницы, если бы вы были немного более откровенны, что неквалифицированный поиск имени, который мы выполняем
flequal
, происходит в контексте определения концепции, а не в контексте определения шаблона функции или даже контекста вызываемого сайтаmain
. На самом деле, это может быть одним из тех редких случаев, когда это может помочь пользователю лучше понять, если мы укажем, что при компиляции определений шаблонов фактически происходит неквалифицированный поиск имен.2. Я начал комментарий с вопроса, может ли моя путаница заключаться в том, что я ожидаю, что требования концепции будут проверены там, где вызов ‘fequal (a, b)’ происходит внутри foo (), поэтому я смущен, когда диагностика говорит: «требование не выполняется, потому что flequal (double, double)не происходит до более позднего ввода единицы перевода «. Означает ли неквалифицированный поиск, что на момент фактического определения требований концепции еще не было никакого объявления flequal(double, double) ?
3. Что касается того, является ли flequal точкой настройки — я не уверен, что это соответствует тому, что на самом деле является точкой настройки. И поскольку приведенный здесь код представляет собой свернутый пример для запуска диагностики, объединенный из заголовка и .cpp, он может не передавать намерения. Но мой план для flequal заключается в том, чтобы сравнивать числа с плавающей запятой, а также любые более крупные классы, сравнения которых в конечном итоге сводятся к сравнениям с плавающей запятой. На мой взгляд, flequal — это операция всего проекта, которая будет перегружена для новых классов, где они определены.
4. @Barry, когда я поменяю определения на
concept Flequalable
иtemplate<floating_point T> bool flequal(T,T)
, он будет построен, как вы сказали. Но затем GCC принимаетbool flequal(Bar<Flequalable auto> const amp;a, Bar<Flequalable auto> const amp;b)
, что, по вашим словам, является недопустимым синтаксисом. Тогда это ошибка GCC? Если я удаляюauto
, но не добавляю преамбулуtemplate<Flequalable T, ...
, компилятор говоритrepro.cpp:23:18: error: expected ‘auto’ or ‘decltype(auto)’ after ‘Flequalable’ 23 | bool flequal(Bar<Flequalable> const amp;a, Bar<Flequalable> const amp;b) { ...