Можно ли определить вызываемое понятие, включающее функции и лямбда-выражения?

Я хочу определить концепцию, которая будет принимать все вызываемые объекты. Вот что я сделал до сих пор:

template<typename F>
concept Func = std::is_function_v<std::remove_pointer_t<std::decay_t<F>>> || (requires (F f) {
    std::is_function_v<decltype(f.operator())>;
});

bool is_callable(Func auto&&) {
    return true;
}

bool is_callable(auto&&) {
    return false;
}

Тем не менее, если я определю их:

auto f = [](auto a, auto b, auto c, auto d, auto e) {
    return a * b * c * d * e;
};

int g(int a, int b) {
    return a + b;
}

is_callable(g) это true, а is_callable(f) это false, это не сработало (хочу, чтобы оба возвращали true).

Поэтому я попытался посмотреть, будет ли компилироваться следующее:

decltype(f.operator()) // Reference to non-static member function must be called
decltype(&f.operator()) // Cannot create a non-constant pointer to member function
decltype(f::operator()) // 'f' is not a class, namespace, or enumeration
decltype(&f::operator()) // same as previously

Это дало мне ошибки, которые вы можете увидеть как комментарий к этим 4 строкам.

Есть ли способ проверить, имеет ли f действительный функтор, который будет означать, что f является лямбдой?

Есть ли лучшее решение для того, чего я пытаюсь достичь?


person titarch    schedule 06.05.2020    source источник
comment
Я хочу определить концепцию, которая будет принимать все вызываемые объекты. Когда вы начинаете определять концепцию, начните с написания кода, который будет использовать эту концепцию. Я не могу представить, как вызываемая концепция может быть полезна без хотя бы списка аргументов. Как вы планируете использовать вызываемый объект, если не знаете, какие аргументы допустимо ему передавать?   -  person Nicol Bolas    schedule 06.05.2020
comment
Кроме того, почему вы делаете is_callable функцией, а не просто самой концепцией?   -  person Nicol Bolas    schedule 06.05.2020
comment
Ну да, я хочу использовать это с функцией curryfied (я отлично реализовал curryfication с концепциями), что затрудняет (или делает невозможным) получение списка аргументов.   -  person titarch    schedule 06.05.2020
comment
Что касается is_callable, я просто нашел более читабельным представить его таким образом, и чтобы код компилировался, а не просто выдавал ошибку.   -  person titarch    schedule 06.05.2020
comment
что затрудняет (или делает невозможным) получение списка аргументов. В какой-то момент кто-то должен иметь список аргументов, чтобы фактически использовать функцию. Это тот момент, когда вы применяете концепцию.   -  person Nicol Bolas    schedule 06.05.2020
comment
curry(f)(1, 2)()(3)(4, 5) да, в конце концов он будет вызываться с аргументами, и в конце концов я вызываю f со всеми аргументами, это не удастся, если f нельзя вызывать да. Мне просто интересно, могу ли я создать концепцию, чтобы сделать ее более строгой и отображать более качественные сообщения об ошибках (что, я думаю, является частью того, почему концепции существуют). Даже если это не очень полезно, я хотел бы знать, возможно это или невозможно.   -  person titarch    schedule 06.05.2020
comment
Без списка аргументов, переданного curry(f), как результирующий функтор карри узнает, что f принимает 5 аргументов?   -  person Nicol Bolas    schedule 06.05.2020


Ответы (1)


То, что вы хотите, невозможно (или это хорошая идея, но неважно, что сейчас).

«Функция» по имени в C++ потенциально представляет множество функций. Он представляет собой перегрузки, создание экземпляров шаблона посредством вывода аргументов шаблона и т. д. Но чтобы получить указатель на функцию, вам необходимо просмотреть все это. Если имя представляет набор перегрузок, чтобы получить указатель, вы должны привести это имя к определенной перегрузке. Если имя представляет собой шаблон, вы должны предоставить аргументы шаблона для обозначения конкретного экземпляра.

Это означает, что к тому времени, когда ваша гипотетическая концепция is_callable будет вызываться для типа указателя функции, все разрешение перегрузки и подстановка шаблона уже выполнены. Ему дается единственный указатель на определенный, четко определенный фрагмент кода, который можно вызывать с сигнатурой, определяемой типом этого указателя.

Ничто из этого не относится к функции object. Функтор (будь то сгенерированный лямбда-выражением C++ или просто написанный от руки тип) — это не что иное, как тип с перегрузкой operator(). И эта перегрузка — это просто имя функции, точно такое же, как и любое другое имя: с учетом правил разрешения перегрузок и подстановки шаблонов.

C++ не позволяет задавать вопрос "вот имя; могу ли я вызвать это чем-то?"

И вообще говоря, это просто бесполезный вопрос.

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

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

person Nicol Bolas    schedule 06.05.2020
comment
Спасибо за ваш ответ, хотя я не согласен с тем фактом, что он должен быть полезным, чтобы его можно было определить. - person titarch; 06.05.2020
comment
@titarch: Зачем тебе бесполезная вещь в языке? C++ уже имеет множество возможностей сомнительного достоинства; нам большего и не надо ;) - person Nicol Bolas; 06.05.2020
comment
Ну, мне просто нравится определять бесполезные вещи, просто чтобы доказать, что это возможно, чтобы посмотреть, допускает ли язык такие вещи, я очень очарован языками на метауровне (может быть, я просто сумасшедший). - person titarch; 06.05.2020
comment
C++ не позволяет задавать вопрос здесь имя; я могу назвать это чем-то? Мы можем сделать это с нефинальными классами, см. ответ в связанном вопросе . - person Jarod42; 16.06.2020