【问题标题】:Compilation fail when apply function_traits in a nested lambda在嵌套 lambda 中应用 function_traits 时编译失败
【发布时间】:2015-01-18 22:00:40
【问题描述】:

首先,我有这样的东西,一个重命名的 function_traits 来获取 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

【问题讨论】:

  • 我试过你的代码(把QString改成int,因为我没有安装Qt),它似乎在g++和clang++中工作,见rextester.com/JZOU33170这似乎是一个VC问题,因为每当您将编译器更改为 VC++ 时都会出现错误

标签: c++ templates visual-c++ lambda


【解决方案1】:

我们可以将例子简化为

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 内的行之前。除了说它不正确之外,我真的无法解释这种行为,因为很明显,类类型不是指向函数的指针类型。错误报告应该是适当的。

一种解决方法是在外部定义 lambda

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

Demo.

【讨论】:

  • 谢谢,我会报告的。有什么办法可以绕过这个吗?
  • 进一步简化:int main() { []{ []{}.operator()(); }; } 这仍然被 gcc 和 clang 接受,但在 MSVC 上失败,并出现“错误 C2228:'.()' 左侧必须有类/结构/联合”
  • @hvd 哦,是的。我认为问题在于我们试图在模板中从实际上下文之外访问成员。
【解决方案2】:

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

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的问题。如果你拿走第三个函数指针的模板,它就不起作用了。

【讨论】:

  • 我遇到了同样的问题,现在可以了!感谢您的解决方案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-07-13
  • 1970-01-01
  • 2014-03-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多