代码之家  ›  专栏  ›  技术社区  ›  Sumant

lambda特性在C++0x编译器中不一致

  •  15
  • Sumant  · 技术社区  · 16 年前

    我观察到两个编译器(g++4.5,VS2010 RC)在匹配lambda和类模板的部分专门化的方式上存在一些不一致。我试图为lambdas实现类似boost::function\u类型的东西来提取类型特征。检查 this 更多细节。

    在g++4.5中 operator() 一个lambda的函数似乎类似于一个独立函数(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上都可以编译,但是实例化的函数和代码中提到的不同。

    3 回复  |  直到 9 年前
        1
  •  3
  •   Potatoswatter    16 年前

    我认为海合会是不合规的。 N3092号§5.1.2/5表示

    的闭包类型 lambda表达式具有公共内联 函数调用运算符(13.5.4),其 参数和返回类型是 由lambda表达式描述 尾随-返回类型。 此函数调用运算符无效 lambda表达式 参数声明子句无效

    因此,虽然闭包对象类型的许多内容都是实现定义的,但函数本身必须是要定义的成员 public const .

    编辑: 这个程序表明 operator()

    #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 . 如果没有捕获,并且只有(除了修复问题之外)似乎改变了call操作符的常量,那么这是没有意义的。

    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 
    

    没有变量和常量, spectfun 不打印最后两个查询的签名。

        2
  •  1
  •   Terry Mahaffey    16 年前

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

        3
  •  0
  •   Earth Engine    15 年前

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