为什么 std::integral_constant<lambda> 的工作方式与 std::integral_constant<function-ptr> 不同?

C.M*_*.M. 7 c++ language-lawyer c++17

(与这个问题相关)。

我正在尝试unique_ptr通过 lambda组合std::integral_constant(在 C++20 中获取大多数 std 函数的地址是非法的,我正在找出一种将它们包装在 lambda 中的便捷方法)。我注意到std::integral_constant我无法解释的奇怪行为(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;
}
Run Code Online (Sandbox Code Playgroud)

有人可以解释为什么第 3 行可以编译吗?这就是std::unique_ptr秘密使用的内容(当你用作std::integral_constant删除器时),这就是为什么我尝试使用 lambda 而不是函数地址失败的原因。

PS 第 4 行失败并显示以下消息:

<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
Run Code Online (Sandbox Code Playgroud)

use*_*522 5

当对对象执行函数调用时,不仅仅考虑调用运算符。如果存在具有合适 cvref 限定符的函数指针类型(或函数引用类型)的非显式转换函数,则存在特殊例外。

在这些情况下,[over.call.object]/2表示生成了一个额外的重载,一个代理调用函数,它将转换后的隐式对象指针作为第一个参数,并将函数指针/引用的参数作为进一步的参数。如果选择此重载,它将使用转换函数转换this为函数指针/引用,然后使用剩余的提供参数调用它。

std::integral_constant有一个非显式转换函数,所以value_type如果value_type是一个函数指针/引用,只有这样,这个代理调用才会存在,它本质上将对象函数调用转发到对存储的函数指针/引用的调用。