Is it possible to define a callable concept that includes functions and lambdas?

▼魔方 西西 提交于 2020-06-16 19:20:13

问题


I want to define a concept that would accept all callable objects. Here's what I have done so far:

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;
}

Yet if I define those:

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) is true but is_callable(f) is false, it did not work (I want both to return true).

So I tried to see if the following would compile:

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

It gave me the errors you can see as comment on those 4 lines.

Is there a way to check is f has a valid functor which would mean f is a lambda?

Is there a better solution to what I am trying to achieve?


回答1:


What you want is not possible (or a good idea, but nevermind that now).

A "function" by name in C++ represents potentially many functions. It represents overloads, template instantiations through template argument deduction, etc. But to get a function pointer, you are required to peer through all of that. If the name represents an overload set, to get a pointer, you have to cast that name to a specific overload. If the name represents a template, you have to provide the template arguments to denote a specific instantiation.

What that means is that, by the time your hypothetical is_callable concept gets to be invoked on a function pointer type, all of the overload resolution and template substitution has already happened. It is given a single pointer to a specific, well-defined piece of code which can be called with a signature defined by the type of that pointer.

None of that is the case for a function object. A functor (whether generated by a C++ lambda expression or just a hand-written type) is nothing more than a type with an operator() overload. And that overload is just a function name, exactly like any other name: subject to rules of overload resolution and template substitution.

C++ does not allow you to ask the question "here's a name; can I call this with something?"

And broadly speaking, that's just not a useful question to ask.

Whether you're using this "callable" concept for currying or whathaveyou, at some point, some piece of code is going to call some function with some set of arguments, which will eventually cascade into calling the given function with another set of argument defined by some process. That is the point when you need to constrain the given callable.

Constraining the function at the site where you're building the curried callable is useless. You have no idea if there's a type mismatch between arguments and return values, or anything of the kind. You will only know that when you're given a set of arguments to use to call the curried callable. That's the place when you can compute the arguments to eventually call the proper function, so that's where the constraining should happen.



来源:https://stackoverflow.com/questions/61641848/is-it-possible-to-define-a-callable-concept-that-includes-functions-and-lambdas

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!