7

(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
Boann
  • 48,794
  • 16
  • 117
  • 146
C.M.
  • 3,071
  • 1
  • 14
  • 33

1 Answers1

5

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.

user17732522
  • 53,019
  • 2
  • 56
  • 105
  • Woah... That is one weird rule. I wonder if it needs to be expanded with `... or or the type “[reference to] lambda ”`? Since lambdas are effectively functions... – C.M. Feb 07 '22 at 18:29
  • @C.M. The rule is already in C++98. It is very specific. Maybe noone has bothered proposing to include lambdas? The rule also doesn't seem to properly forward the value category of the arguments, another C++11 feature for which it seems to not have been updated. – user17732522 Feb 07 '22 at 18:41
  • @C.M. no, lambdas are not "effectively functions". That's a wrong generalization. Lambdas create closure objects. That's much more complex. – JHBonarius Feb 07 '22 at 18:43
  • @JHBonarius It looks like this rule was introduced to allow function pointers to be magically called in such situations. I think, in this context extending this magic to lambdas makes sense (even though they are significantly more complicated). Or remove this rule altogether and/or replace it (in `unique_ptr/etc`) with mechanism that recognizes callable objects. – C.M. Feb 07 '22 at 18:50
  • @C.M. Regarding `std::unqiue_ptr`: You just have to define your own `std::integral_constant` whose `operator()` forwards the arguments to the stored object, rather than returning the object. Then it will work as deleter. – user17732522 Feb 07 '22 at 18:54
  • @user17732522 Yes, or event better: `unique_ptr`, maybe add some utility that will make making these more convenient... – C.M. Feb 07 '22 at 19:27
  • Comically, [explicitly casting lambda to function pointer](https://godbolt.org/z/PnE337c4b) fixes MSVC, but breaks GCC... – C.M. Feb 07 '22 at 21:15
  • 1
    @C.M. Seems to be [this bug in GCC](https://gcc.gnu.org/bugzilla/show_bug.cgi?id=83258). – user17732522 Feb 07 '22 at 21:36
  • @user17732522 Thank you – C.M. Feb 07 '22 at 22:53