将重载的 CRTP 类成员方法传递给 lambda

Hi *_* SO 10 c++ lambda overloading crtp c++17

考虑一下:

template<typename T>
struct base_t
{
    auto& f(int x) { return (T&)*this; }
    auto& f(char x) { return (T&)*this; }
};

struct derived_t : base_t<derived_t>
{

};

void test()
{
    derived_t instance;

    auto lambda = [&](derived_t&(derived_t::*g)(char))
    {
        (instance.*g)('a');

        //instance.f('a');
    };

    lambda(&derived_t::f);
}
Run Code Online (Sandbox Code Playgroud)

没有在该特定行 ( //instance.f('a');) 中发表评论,我收到以下错误 (MSVC 2019):

error C2664: 'void test::<lambda_1>::operator ()(derived_t &(__cdecl derived_t::* )(char)) const': cannot convert argument 1 from 'overloaded-function' to 'derived_t &(__cdecl derived_t::* )(char)'
Run Code Online (Sandbox Code Playgroud)

当该行没有被注释掉时,它编译得很好。

为什么引用finsidelambda神奇地允许编译器转换这个重载函数?

此外,如果没有 CRTP,这根本不会发生。

编辑:此外,正如@Jarod42 所指出的,

  • 明确返回类型 auto& -> T& 解决了这个问题。

  • 如果您使用命名函数而不是 lambda,问题就会消失。所以显然 lambda 和模板的交互是相关的。

Vic*_*gue 3

模板机制在使用类和函数时实例化它们。使用相同的机制来评估关键字后面的类型auto

在您的情况下,函数的返回类型base_t<T>::fauto&, 并且需要计算函数调用。因此,当您注释掉对它的唯一调用 ( instance.f('a');) 时,无法计算该函数的实际签名,并且编译器无法判断它是否可以转换为derived_t&(derived_t::*g)(char).

如果您按如下方式instance.f('a');定义函数,则可以注释掉:base_t<T>::f

template<typename T>
struct base_t
{
    T& f(int) { return *static_cast<T*>(this); }
    T& f(char) { return *static_cast<T*>(this); }
};
Run Code Online (Sandbox Code Playgroud)

这里类型是在专门类型的实例化base_t<derived_t>时而不是在调用函数时推导f的,因此编译器可以计算出其到函数类型的转换,derived_t&(derived_t::*g)(char)而无需在代码中调用它们。