C++0x 编译器之间的 lambda 特征不一致

发布于 2024-08-27 23:59:28 字数 1097 浏览 9 评论 0原文

我观察到两个编译器(g++ 4.5、VS2010 RC)将 lambda 与类模板的部分特化相匹配的方式存在一些不一致。我试图为 lambda 实现类似 boost::function_types 的东西来提取类型特征。检查了解更多详细信息。

在 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 与代码中所述不同。

I observed some inconsistency between two compilers (g++ 4.5, VS2010 RC) in the way they match lambdas with partial specializations of class templates. I was trying to implement something like boost::function_types for lambdas to extract type traits. Check this for more details.

In g++ 4.5, the type of the operator() of a lambda appears to be like that of a free standing function (R (*)(...)) whereas in VS2010 RC, it appears to be like that of a member function (R (C::*)(...)). So the question is are compiler writers free to interpret any way they want? If not, which compiler is correct? See the details below.

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 *
}

This program compiles on both g++ 4.5 and VS2010 but the function_traits that are instantiated are different as noted in the code.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

空心空情空意 2024-09-03 23:59:28

我认为 GCC 不合规。 N3092 §5.1.2/5 说

闭包类型
lambda 表达式有一个公共内联
函数调用运算符 (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 表达式可变。如果没有捕获,并且仅(除了解决问题之外)似乎改变了调用运算符的常量性,那么这是毫无意义的。

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 不会打印最后两个查询中任何一个的签名。

I believe that GCC is noncompliant. N3092 §5.1.2/5 says

The closure type for a
lambda-expression has a public inline
function call operator (13.5.4) whose
param- eters and return type are
described by the lambda-expression’s
parameter-declaration-clause and
trailing- return-type respectively.
This function call operator is
declared const (9.3.1) if and only if
the lambda- expression’s
parameter-declaration-clause is not
followed by mutable.

So while many things about the closure object's type are implementation-defined, the function itself must be a member to be public and must be a nonstatic member to be const.

EDIT: This program indicates that operator() is a member function on GCC 4.6, which is essentially the same as 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;
}

output:

member of Z4mainEUlvE_, returns i takes i

EDIT: It looks like the only problem is that pointers to certain closure call operators fail to match ptmf template patterns. The workaround is to declare the lambda expression mutable. This is meaningless if there is no capture and only (aside from fixing the problem) seems to change the const-ness of the call operator.

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;

output:

returns i takes i 
member of Z4mainEUliE0_ , returns i takes i 
member of Z4mainEUliE0_ , returns i takes i 

Without the mutable and with the const, spectfun does not print signatures for either of the last two queries.

静水深流 2024-09-03 23:59:28

阅读n3043。 Lambda 现在可以转换为函数指针,前提是它们没有任何状态。我相信(...但不知道)GCC 最初意外地实现了此行为,“修复了它”,现在将其重新添加到 4.5 或 4.6。 VC10 按照最初设计正确实现了 lambda,但不符合 n3043 的最新工作文件。

Read n3043. Lambdas are now convertible to function pointers provided they don't have any state. I believe (...but do not know) GCC initially implemented this behavior accidentally, "fixed it", now will be re-adding it to 4.5 or 4.6. VC10 implemented lambdas correctly as initially designed, but not conforming to the latest working papers with n3043.

兔姬 2024-09-03 23:59:28

我认为 gcc 开发人员的这种行为是有充分理由的。请记住,静态函数没有“this”指针,并且当实际调用它时,调用者不需要传递“this”指针。因此,当闭包对象中实际上不包含任何内容时,这是一个小的性能优化。您可以看到 G++ 开发人员通过将 lambda 表达式声明为“可变”(记住您实际上没有任何内容可以改变)为您提供了一种解决方法。

I think gcc developers has a good reason for this behaivor. Remember, a static function do not have a "this" pointer, and when it is being actually called, the caller do not required to pass the "this" pointer. So this is a small performance optimisation when it is actually nothing contained in the closure object. And you can see the G++ developer leave you a way to workaround by declaring the lambda expression as "mutable" (remember you actually do not have anything to mutate).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文