我发现两个编译器(g ++ 4.5,VS2010 RC)之间存在一些不一致的方式,它们将lambdas与类模板的部分特化相匹配。我试图为lambdas实现类似boost :: function_types的东西以提取类型特征。查看this了解详情。
在g ++ 4.5中,lambda的operator()
的类型看起来像一个独立函数(R(*)(...))的类型,而在VS2010 RC中,它看起来像成员函数(R(C :: *)(...))。所以问题是编译器编写者可以自由地解释他们想要的任何方式吗?如果没有,哪个编译器是正确的?请参阅下面的详细信息。
template <typename T>
struct function_traits
: function_traits<decltype(&T::operator())>
{
// This generic template is instantiated on both the compilers as expected.
};
template <typename R, typename C>
struct function_traits<R (C::*)() const> { // inherits from this one on VS2010 RC
typedef R result_type;
};
template <typename R>
struct function_traits<R (*)()> { // inherits from this one on g++ 4.5
typedef R result_type;
};
int main(void) {
auto lambda = []{};
function_traits<decltype(lambda)>::result_type *r; // void *
}
这个程序在g ++ 4.5和VS2010上编译,但实例化的function_traits是不同的,如代码中所述。
答案 0 :(得分:4)
我认为海湾合作委员会不合规。N3092§5.1.2/ 5说
a的闭包类型 lambda-expression有一个公共内联 函数调用运算符(13.5.4) 参数和返回类型是 由lambda表达式描述 参数声明子句和 尾随 - 返回类型。 这个函数调用运算符是 声明const(9.3.1)当且仅当 lambda表达式 参数声明子句不是 其次是可变的。
因此,虽然关于闭包对象类型的许多内容都是实现定义的,但函数本身必须是public
的成员,并且必须是const
的非静态成员。
编辑:此程序表明operator()
是GCC 4.6上的成员函数,与4.5基本相同。
#include <iostream>
#include <typeinfo>
using namespace std;
template< class ... > struct print_types {};
template<> struct print_types<> {
friend ostream &operator<< ( ostream &lhs, print_types const &rhs ) {
return lhs;
}
};
template< class H, class ... T > struct print_types<H, T...> {
friend ostream &operator<< ( ostream &lhs, print_types const &rhs ) {
lhs << typeid(H).name() << " " << print_types<T...>();
return lhs;
}
};
template< class T >
struct spectfun {
friend ostream &operator<< ( ostream &lhs, spectfun const &rhs ) {
lhs << "unknown";
return lhs;
}
};
template< class R, class ... A >
struct spectfun< R (*)( A ... ) > {
friend ostream &operator<< ( ostream &lhs, spectfun const &rhs ) {
lhs << "returns " << print_types<R>()
<< " takes " << print_types<A ...>();
return lhs;
}
};
template< class C, class R, class ... A >
struct spectfun< R (C::*)( A ... ) > {
friend ostream &operator<< ( ostream &lhs, spectfun const &rhs ) {
lhs << "member of " << print_types<C>() << ", " << spectfun<R (*)(A...)>();
return lhs;
}
};
template< class T >
struct getcall {
typedef decltype(&T::operator()) type;
};
int main() {
int counter = 0;
auto count = [=]( int ) mutable { return ++ counter; };
cerr << spectfun< getcall<decltype(count)>::type >() << endl;
}
输出:
member of Z4mainEUlvE_, returns i takes i
编辑:看起来唯一的问题是指向某些闭包调用操作符的指针无法匹配ptmf模板模式。解决方法是声明lambda表达式mutable
。如果没有捕获,这是没有意义的,只有(除了修复问题)似乎改变了调用操作符的常量。
template< class T >
struct getcall {
typedef decltype(&T::operator()) type;
static type const value;
};
template< class T >
typename getcall<T>::type const getcall<T>::value = &T::operator();
int main() {
auto id = []( int x ) mutable { return x; };
int (*idp)( int ) = id;
typedef decltype(id) idt;
int (idt::*idptmf)( int ) /* const */ = getcall< decltype(id) >::value;
cerr << spectfun< decltype(idp) >() << endl;
cerr << spectfun< decltype(idptmf) >() << endl;
cerr << spectfun< getcall<decltype(id)>::type >() << endl;
输出:
returns i takes i
member of Z4mainEUliE0_ , returns i takes i
member of Z4mainEUliE0_ , returns i takes i
如果没有mutable和const,spectfun
不会打印最后两个查询中的任何一个的签名。
答案 1 :(得分:1)
阅读n3043。 Lambda现在可以转换为函数指针,前提是它们没有任何状态。我相信(......但不知道)GCC最初意外地实施了这种行为,“修复它”,现在将它重新添加到4.5或4.6。 VC10在初始设计时正确实现了lambdas,但不符合n3043的最新工作文件。
答案 2 :(得分:0)
我认为gcc开发人员有充分的理由来表达这种行为。请记住,静态函数没有“this”指针,当实际调用它时,调用者不需要传递“this”指针。因此,当它实际上没有包含在闭包对象中时,这是一个小的性能优化。你可以看到G ++开发人员通过将lambda表达式声明为“可变”来为你提供一种解决方法(记住你实际上没有任何东西可以改变)。