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

问题描述

考虑一下:

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);
}

没有在该特定行(//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)'

当该行不被注释掉时,它可以很好地编译。

为什么在f中引用lambda会神奇地允许编译器转换此重载函数?

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

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

  • 在返回类型为auto&-> T&时明确显示此问题。

  • 如果使用命名函数而不是lambda,问题将消失。因此,显然lambda与模板之间的交互是相关的。

解决方法

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

在您的情况下,base_t<T>::f函数的返回类型为auto&,并且需要计算函数调用。 因此,当您注释掉对它的唯一调用(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); }
};

这里的类型是在专用类型base_t<derived_t>的实例中推导出的,而不是在f函数的调用中推导出的,因此编译器可以弄清楚其向函数类型derived_t&(derived_t::*g)(char)的转换而无需必须在您的代码中调用它们。

相关问答

依赖报错 idea导入项目后依赖报错,解决方案:https://blog....
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下...
错误1:gradle项目控制台输出为乱码 # 解决方案:https://bl...
错误还原:在查询的过程中,传入的workType为0时,该条件不起...
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct...