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

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

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 является лямбда?

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

1 Ответ

3 голосов
/ 06 мая 2020

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

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

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

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

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

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

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

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

...