在嵌套 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的问题。如果你拿走函数指针的第三个模板,它就不起作用了。
首先,我有这样的东西,重命名 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的问题。如果你拿走函数指针的第三个模板,它就不起作用了。