So, since P1214 isn't likely to move before we can present actual numbers from an actual implementation in a compiler, I'm opening this issue to gather all the feedback & numbers I can provide. That way when it may have chances to pass EWGI and not to die another sad death before EWG.
No need for the bloated <functional>
One thing that the paper does not mention is that <functional>
is often included for std::invoke
only [citation needed] and that <functional>
is known for being one of the slowest headers to parse:
Modules might change this, but the world probably won't fully move to modules until build systems and IDEs are ready to handle them.
std::invoke
is comparatively slow to compile
According to the benchmarks of Eggs.Invoke, std::invoke
is 1.5 to 18 times slower to compile than an equivalent plain function call, and the compilation process uses up to 12 times as much memory. It is quite underwhelming for such a small fundamental function.
Better error messages
The feature isn't implemented yet in compilers, but using normal functions as examples should give an example of how error messages can be improved (is there a simple way to make Tony Tables GitHub issues?):
#include <functional>
void func(int) {}
int main()
{
struct foo_t {} foo;
std::invoke(func, foo);
}
This snippet gives the following output on the Compiler Explorer with GCC 8.2 :
<source>: In function 'int main()':
<source>:8:26: error: no matching function for call to 'invoke(void (&)(int), main()::foo_t&)'
std::invoke(func, foo);
^
In file included from <source>:1:
/opt/compiler-explorer/gcc-8.2.0/include/c++/8.2.0/functional:78:5: note: candidate: 'template<class _Callable, class ... _Args> std::invoke_result_t<_Callable, _Args ...> std::invoke(_Callable&&, _Args&& ...)'
invoke(_Callable&& __fn, _Args&&... __args)
^~~~~~
/opt/compiler-explorer/gcc-8.2.0/include/c++/8.2.0/functional:78:5: note: template argument deduction/substitution failed:
With Clang 7:
<source>:8:5: error: no matching function for call to 'invoke'
std::invoke(func, foo);
^~~~~~~~~~~
/opt/compiler-explorer/gcc-8.2.0/lib/gcc/x86_64-linux-gnu/8.2.0/../../../../include/c++/8.2.0/functional:78:5: note: candidate template ignored: substitution failure [with _Callable = void (&)(int), _Args = <foo_t &>]: no type named 'type' in 'std::invoke_result<void (&)(int), foo_t &>'
invoke(_Callable&& __fn, _Args&&... __args)
^
1 error generated.
And with MSVC 19.16:
<source>(8): error C2672: 'std::invoke': no matching overloaded function found
<source>(8): error C2893: Failed to specialize function template 'unknown-type std::invoke(_Callable &&,_Types &&...) noexcept(<expr>)'
<source>(8): note: With the following template arguments:
<source>(8): note: '_Callable=void (__cdecl &)(int)'
<source>(8): note: '_Types={main::foo_t &}'
Now take the same snippet but without std::invoke
:
void func(int) {}
int main()
{
struct foo_t {} foo;
func(foo);
}
Here is the error message with GCC 8.2:
<source>: In function 'int main()':
<source>:6:10: error: cannot convert 'main()::foo_t' to 'int'
func(foo);
^~~
<source>:1:11: note: initializing argument 1 of 'void func(int)'
void func(int) {}
^~~
With Clang 7:
<source>:6:5: error: no matching function for call to 'func'
func(foo);
^~~~
<source>:1:6: note: candidate function not viable: no known conversion from 'struct foo_t' to 'int' for 1st argument
void func(int) {}
^
And with MSVC 19.16:
<source>(6): error C2664: 'void func(int)': cannot convert argument 1 from 'main::foo_t' to 'int'
<source>(6): note: No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called
It should give a good idea of how the proposal should be able to improve error messages. It is worth noting that no compiler actually points to the definition of func
on failure when std::invoke
is used.
A better debugging experience
Some areas such as the video game industry have a need for interactive debugging and fast debug builds. Release builds tend to collapse calls to std::invoke
into the appropriate underlying function call, but debug builds need to retain information about std::invoke
and its helper functions.
Having language support for callables removes the aforementioned unneeded extra debug information - and hence can help reducing the size of the debug binary -, but also allows for a smoother navigation through function calls when debugging. In the current state of things, debugger implementers can "ignore" std::invoke
as some already ignore std::move
and std::forward
and jump into std::function::operator()
for the sake of a smoother debug navigation, but having language support would give the same experience without requiring implementers to tweak their debuggers.
Once generic libraries start moving away from std::invoke
to generic language callables, it might drive their adoption in the areas that require a smoother debugging experience.
Links about issues with the debugging experience:
There might be more special cases in the future
In P0847R2, it is mentioned that the new kinds of member functions proposed don't follow the usual rules of member function pointers and give the following example:
struct Y {
int f(int, int) const&;
int g(this Y const&, int, int);
};
Y y;
y.f(1, 2); // ok as usual
y.g(3, 4); // ok, this paper
auto pf = &Y::f;
pf(y, 1, 2); // error: pointers to member functions are not callable
(y.*pf)(1, 2); // okay, same as above
std::invoke(pf, y, 1, 2); // ok
auto pg = &Y::g;
pg(y, 3, 4); // okay, same as above
(y.*pg)(3, 4); // error: pg is not a pointer to member function
std::invoke(pg, y, 3, 4); // ok
This means that depending on how you declare your member function, you might get different results on how it is possible to invoke a pointer to it. I think that it's yet another case where P1214 makes things better overall. It might be something worth mentioning in P0847 if the authors are fine with it.
It makes library implementers life easier
Once library implementers (including standard library ones) start relying on this feature, it will free there time to implement you favourite features faster! :D
(don't include this one in the paper)