Different style of function "pointer": lifting an overload set into a lambda

60 views
Skip to first unread message

Justin Bassett

unread,
Jul 15, 2017, 1:29:29 AM7/15/17
to ISO C++ Standard - Future Proposals
This seems to be almost the same as Suggesting a syntax for wraping overload set .

Currently in C++, we can get a function pointer with &ns::foo or ns::foo. The problem with this is that, if foo is overloaded, it fails to compile as it actually needs to produce a pointer to a concrete function. I find this unintuitive. To get the behavior I would expect, one would need to write:

[](auto&&... args) { return ns::foo(std::forward<decltype(args)>(args)...); }

Or in the truly general case:

[](auto&&... args) noexcept(noexcept(ns::foo(std::forward<decltype(args)>(args)...)))
               
-> decltype(ns::foo(std::forward<decltype(args)>(args)...)) {
   
return ns::foo(std::forward<decltype(args)>(args)...);
}

Or, using Barry's Abbreviated Lambdas:

[](args...) => ns::foo(>>args...)

In that paper, it was mentioned that there is an idea floating around to make this:

[]ns::foo

I think this should be added to C++.

Furthermore, if Make Pointers to Members Callable happens, it makes sense to have the corresponding thing for member pointers:

[]ns::Class::member

Which would be roughly equivalent to:

[](auto&& cls, auto&&... args) {
   
static_assert(std::is_same_v<ns::Class, std::decay_t<decltype(cls)>>);
    return std::forward<decltype(cls)>(cls).member(std::forward<decltype(args)>(args)...);
}

But it would make sense to allow the cls parameter to be the same as in std::invoke (as in the Make Pointers to Members Callable paper). That is, if cls were actually a ns::Class*, it would work, as well as a std::reference_wrapper<ns::Class> and the rest of the invoke protocol.

This has an immediate benefit: you can make this type of function reference to standard library objects. &std::string::size is illegal, as the standard library is allowed to have default arguments, but []std::string::size would be legal.

There are possible drawbacks. For one, what should the following be:

void foo() {}

int main() {
   
auto foo = []{};
   
auto bar = []foo;
}

Should it capture
foo, call the foo function, or do something else? I'd suggest that it would call the foo function, and that if the user wanted to capture it would be:

int main() {
    
auto foo = []{};
    
auto bar = [foo]foo; // or [&foo]foo to capture by reference
}

But that might not be feasible.

Nicol Bolas

unread,
Jul 15, 2017, 10:41:01 AM7/15/17
to ISO C++ Standard - Future Proposals
On Saturday, July 15, 2017 at 1:29:29 AM UTC-4, Justin Bassett wrote:
This seems to be almost the same as Suggesting a syntax for wraping overload set .

There has already been a proposal (actually two) submitted suggesting similar things. I don't know what happened to them, but I wish they'd move forward. We all seem to know that we need it, but it never seems to advance.


I would go farther. Remember: the idea is that `[]name` becomes a functor such that invoking it is exactly equivalent to `name(...)`. If `name` refers to an object in local scope rather than a function name, then `[]name` should be il-formed. Ambiguity can always be resolved by explicit syntax; I'd rather we not

So if you want to capture the `foo` object and call it, then you need to actually capture the `foo` object and call it: `[foo](...) {return foo(...);}`. Shorthand notation can be used to expedite this, but I don't think we need it to be as short as `[foo]foo`.

Also, we should allow class constructors to be wrapped via `[]typename`. It should even work for non-class types.
Reply all
Reply to author
Forward
0 new messages