(related to this question).
I am trying to combine unique_ptr
and lambda via std::integral_constant
(taking address of most std functions is outlawed in C 20, I am figuring out a convenient way to wrap them in lambda). I noticed weird behaviour of std::integral_constant
that I can't explain (godbolt):
#include <type_traits>
template<auto L, class T = decltype(L)>
using constant = std::integral_constant<T, L>;
void dummy(void*);
int main()
{
using C1 = constant<&dummy>;
using C2 = constant<[](void* p){ dummy(p); }>;
C1()()(nullptr); // #1 works as expected
C2()()(nullptr); // #2 works as expected
C1()(nullptr); // #3 unexpectedly works
C2()(nullptr); // #4 fails as expected
return 0;
}
Can someone explain why line #3 compiles? This is what std::unique_ptr
uses under cover (when you use std::integral_constant
as deleter) and this is why my attempts to use lambda instead of function address fail.
P.S. Line #4 fails with following message:
<source>: In function 'int main()':
<source>:17:17: error: no match for call to '(C2 {aka std::integral_constant<const main()::<lambda(void*)>, <lambda closure object>main()::<lambda(void*)>{}>}) (std::nullptr_t)'
17 | C2()(nullptr); // #4 fails as expected
CodePudding user response:
When performing a function call on an object, not only the call operators are considered. There is a special exception if a non-explicit conversion function to a function pointer type (or function reference type) with suitable cvref-qualifiers exists.
In these situations [over.call.object]/2 says that an additional overload is generated, a surrogate call function which takes the converted implicit object pointer as first argument and the function pointer/reference's parameters as further parameters. If this overload is chosen, it will use the conversion function to convert this
to the function pointer/reference and then call it with the remaining provided arguments.
std::integral_constant
has a non-explicit conversion function to value_type
and so if value_type
is a function pointer/reference, and only then, will this surrogate call exist, which essentially forwards the object function call to a call to the stored function pointer/reference.