Концепции C ++ - концепция в скобках «требует» вызывает 2 конфликтующих сообщения об ошибках. - PullRequest
1 голос
/ 28 мая 2020

Я много работал сегодня утром, чтобы понять концепции немного больше (еще Newb ie), и я наткнулся на что-то странное.

1002 * Я хотел понять, как хорошо концепции работы с Lambda функций, я много пытался с ними поиграть.

Давайте начнем с этого кода (который работает!):

#include <concepts>
#include <stdio.h>

template <typename T>
concept Integral =
requires(T n)
{
    { n } -> std::convertible_to<int>;
};

template <typename Operation>
concept CalculatorOperation =
requires(Operation&& op, int a, int b)
{
    { op(a, b) } -> Integral;
};

template <CalculatorOperation Operation>
static inline constexpr Integral auto Calculator(const Integral auto First,
                                                 const Integral auto Second)
{
    return Operation{}(First, Second);
}

int main()
{
    static constexpr auto Multiply = [](const Integral auto First,
                                        const Integral auto Second)
                       { return First * Second; };
    return Calculator<decltype(Multiply)>(15, 18);
}

Я создал интегральную концепцию, которая (просто для манекена ) выполняется, если тип конвертируется в int. Я создал концепцию операции калькулятора, которая принимает лямбду, два целых числа и проверяет, что операция возвращает интеграл.

Я решил, что мне нужен int в концепции превратиться в Integral , поскольку я не хочу, чтобы в операцию принимались только int (мне нужны все интегральные типы).

Итак Я изменил

requires(Operation&& op, int a, int b)

на

requires(Operation&& op, Integral&& a, Integral&& b)

Мало того, что это не сработало для меня, я получил 2 конфликтующих сообщения об ошибках (на G CC 10.1):

<source>:13:30: error: placeholder type not allowed in this context


<source>:13:30: error: expected 'auto' or 'decltype(auto)' after 'Integral'

   13 |     requires(Operation&& op, Integral&& a, Integral&& b)

Я запускал это с помощью Compiler Explorer веб-сайта GodBolt. Что тут происходит? Есть ли способ сделать это?

1 Ответ

2 голосов
/ 28 мая 2020

Во-первых, делая это:

requires(Operation&& op, Integral&& a, Integral&& b)

вы, вероятно, имели в виду это:

requires(Operation op, Integral auto a, Integral auto b)

то есть, чтобы использовать ограничение типа auto форма описателя типа заполнителя.

Однако параметры auto создают шаблоны функций, а заполнитель auto является эквивалентом фактического параметра шаблона придуманного типа этого шаблона функции, поэтому он может отображаться только в контекстах, где происходит вывод типа из инициализатора, и это означает, что:

void foo(auto x);

является эквивалентом:

template <typename T>
void foo(T x);

Аналогично,

void bar(Integral auto x);

является эквивалентом:

template <Integral T>
void bar(T x);

Такое преобразование не применимо к выражению requires, т. е. отсутствует инициализатор, тип которого можно было бы определить, и поэтому нельзя использовать заполнитель auto в объявлении параметра списка параметров требований. Эти параметры, как сказано в стандарте, «используются только в качестве обозначения с целью определения требований». Таким образом, вместо этого вам необходимо явно указать все аргументы шаблона, которые необходимы для работы концепции, например:

template <typename Operation, typename Lhs, typename Rhs>
concept CalculatorOperation = requires(Operation op, Lhs a, Rhs b)
{
    { op(a, b) } -> Integral;
};

template <typename Operation>
constexpr Integral auto Calculator(Integral auto first, Integral auto second)
    requires CalculatorOperation<Operation, decltype(first), decltype(second)>
{
    return Operation{}(first, second);
}

Также обратите внимание, что я удалил && из каждого объявления параметра в списке параметров требований. Все выражение requires - это неоцененный контекст, и аргументы фактически не передаются.

...