Я пытаюсь реализовать что-то вроде Haskell Prelude на С++ с классами типов, такими как Functor, Monoid и т.д. Поэтому я решил использовать C++ Concepts для проверки типов, но столкнулся с проблемой при попытке реализовать концепцию Semigroup.
Проблема возникает, когда я пытаюсь объявить концепцию, использующую некоторую функцию, которая объявлена и перегружена для необходимых типов позже в коде, но она актуальна только для некоторых типов, таких как std::vector, но для моих собственных типов работает хорошо.
Полный код:
template<class T>
struct S {
// ...
};
template<class T>
concept Addable = requires(T a, T b) {
{ add(a, b) } -> T;
};
template<class T>
S<T> add(const S<T> & a, const S<T> & b) {
// ...
return {};
}
template<class T>
std::vector<T> add(const std::vector<T> & a, const std::vector<T> & b) {
// ...
return {};
}
int main() {
std::cout << Addable<S<int>> << '\n';
std::cout << Addable<std::vector<int>> << '\n';
}
Я ожидаю результат 1 1, но фактический результат 1 0.
Таким образом, он не распознает std::vector как Addable независимо от объявленной перегрузки add(std::vector), но распознает тип S как Addable.
УПД:
Проблема исчезнет, если я передвину функцию add перед понятием Addable. Но выглядит очень неудобно и неясно, что я должен реализовать все концепции, которые я включаю для таких типов, как std::vector, прежде чем я включу эти концепции.
УПД2: Я использую GCC 9.1.0.
@chris Как я и думал - как только вы переместите Addable вниз, чтобы add было видно при обычном поиске, программа напечатает 1 1
Обратите внимание, что Clang предоставит вам дополнительную информацию о Зачем, если вы не пройдете проверку разрешения перегрузки как видно здесь.
@IgorTandetnik Значит ли это, что если у меня есть std::vector из stl и какой-то Addable концепт из сторонней библиотеки, я не смогу реализовать этот концепт для vector? Или я могу сделать это, только поставив add перед включением заголовка с Addable?





Я думаю, что это дефект экспериментальной реализации концепции Clang. [темп.концепт]/8 говорит:
A concept is not instantiated ([temp.spec]).
См. также: В каком контексте управления доступом оцениваются концепции?
Это означает, что разрешение имен, как оно описано в [темп.рез], не применяется к выражению, которое является определением понятия. Если бы понятия должны были быть созданы, то функция add не могла бы быть найдена с помощью поиска без уточнения имени. Вот почему Clang выдает ошибку.
В соответствии со стандартом, когда id-выражение называет концепцию, выражение-ограничение нормализованный оценивается там, где появляется id-выражение ([expr.prim.id]/4).
Менее формально выражение, являющееся определением понятия, оценивается в контексте выражения, именующего понятие. Таким образом, Addable<vector<int>> должно быть истинным, потому что в контексте этого выражения add можно найти с помощью поиска без уточнения имени.
Это не специфично для clang, gcc 9.1 дает то же самое.
@bipil Оригинальные концепции, в которых используются функции или переменные. Пункт I сайта относительно новый: он был добавлен в стандарт в прошлом году и не является частью концепта TS.
Я предполагаю, что
AddableнаходитaddдляSс помощью поиска, зависящего от аргумента, но не может сделать то же самое дляvector, поскольку класс и функция находятся в разных пространствах имен. Будет ли работать, если вы объявите или определитеaddпередAddable?