在嵌套 lambda 中应用 function_traits 时编译失败

Compilation fail when apply function_traits in a nested lambda

首先,我有这样的东西,重命名 function_traits 以获得 return 类型的 lambda

template <typename T>
struct FuncAnalyzer
{
};

template <typename T, typename TRet, typename... TArgs>
struct FuncAnalyzer<TRet(T::*)(TArgs...) const>
{
    using TReturn = TRet;
};

template <typename T>
struct FunctionAnalyzer
    : public FuncAnalyzer<decltype(&T::operator())>
{
};

然后当我在一个方法中有这个时,那个 compi:

auto a = [](const int& key) -> QString { return QString::number(key); };
using b = FunctionAnalyzer<decltype(a)>::TReturn;
b x;

但是当我尝试将它放入 lambda 中时,它不起作用

    auto c = [](const int& key) -> QString 
    { 
        auto a = [](const int& key) -> QString { return QString::number(key); };
        using b = FunctionAnalyzer<decltype(a)>::TReturn;
        b x;
        return QString::number(key); 
    };

编译输出:

1>i:\uicgraph\common\FunctionAnalyzer.h(21): error C2825: 'T': must be a class or namespace when followed by '::'
1>          Controller\Schema\SchemaController.cpp(105) : see reference to class template instantiation 'ValidSig::FunctionAnalyzer<QString (__cdecl *)(const int &)>' being compiled
1>i:\uicgraph\common\FunctionAnalyzer.h(21): error C2039: '()' : is not a member of '`global namespace''
1>i:\uicgraph\common\FunctionAnalyzer.h(21): error C2275: 'T' : illegal use of this type as an expression
1>          Controller\Schema\SchemaController.cpp(105) : see declaration of 'T'
1>          Controller\Schema\SchemaController.cpp(105) : see declaration of 'T'
1>i:\uicgraph\common\FunctionAnalyzer.h(21): error C2146: syntax error : missing ')' before identifier '()'
1>i:\uicgraph\common\FunctionAnalyzer.h(21): error C2143: syntax error : missing ',' before ')'
1>i:\uicgraph\common\FunctionAnalyzer.h(21): error C2947: expecting '>' to terminate template-argument-list, found '>'
1>Controller\Schema\SchemaController.cpp(105): error C2039: 'TReturn' : is not a member of 'ValidSig::FunctionAnalyzer<QString (__cdecl *)(const int &)>'
1>Controller\Schema\SchemaController.cpp(105): error C2061: syntax error : identifier 'TReturn'

我正在使用 MSVC 2013

我们可以将示例简化为

template <class T> struct identity {using type = T;};

template <typename T>
struct FunctionAnalyzer
    : identity<decltype(&T::operator())> {};

int main()
{
    []
    {
        auto a = []{};
        using b = FunctionAnalyzer<decltype(a)>::type;
    }();
}

这个编译很好with GCC and Clang
然而,显然 VC++ 错误地传递了 void (__cdecl *)(void) 而不是实际的闭包类型作为模板参数——即使静态断言如

    static_assert( std::is_class<decltype(a)>::value, "" );

就在 lambda succeeds 内的行之前。除了说它不正确之外,我真的无法解释这种行为,因为显然 class 类型不是指向函数的指针类型。错误报告应该是适当的。

解决方法是在外部定义 lambda

auto a = []{};
[a] // This is not required by standard! VC++ being stupid I guess
{
    using b = FunctionAnalyzer<decltype(a)>::type;
}();

Demo.

好的,重写模板后问题解决

template <typename TFunc>
struct FunctionAnalyzer
{
    using ReturnType = typename FunctionAnalyzer<decltype(&TFunc::operator())>::ReturnType;
};

template <typename TClass, typename TRet, typename... TArgs>
struct FunctionAnalyzer<TRet(TClass::*)(TArgs...) const>
{
    using ReturnType = TRet;
};

template <typename TRet, typename... TArgs>
struct FunctionAnalyzer<TRet(*)(TArgs...)>
{
    using ReturnType = TRet;
};

看来是VS2013的问题。如果你拿走函数指针的第三个模板,它就不起作用了。