несоответствие черт лямбды через C++ 0x компиляторы

Я наблюдал некоторое несоответствие между двумя компиляторами (g ++ 4.5, VS2010 RC) в способе, которым они соответствуют лямбдам частичным специализациям шаблонов классов. Я пытался реализовать что-то как повышение:: function_types для лямбд для извлечения черт типа. Проверьте это на большее количество деталей.

В g ++ 4.5, тип operator() из лямбды, кажется, похож на ту из бесплатной постоянной функции (R (*)..(.)), тогда как в VS2010 RC, это, кажется, похоже на VS2010 RC функции членства (R (C::*) (...)). Таким образом, вопросом являются действительно ли разработчики компилятора, свободны интерпретировать какой-либо способ, которым они хотят? В противном случае, какой компилятор корректен? Посмотрите детали ниже.

template 
struct function_traits 
  : function_traits 
{ 
// This generic template is instantiated on both the compilers as expected.
};

template 
struct function_traits  { // inherits from this one on VS2010 RC
  typedef R result_type;
};

template 
struct function_traits { // inherits from this one on g++ 4.5
  typedef R result_type;
};

int main(void) {
  auto lambda = []{};
  function_traits::result_type *r; // void *
}

Эта программа компиляции и на g ++ 4.5 и на VS2010, но function_traits, которые инстанцируют, отличается, как отмечено в коде.

15
задан Community 23 May 2017 в 11:47
поделиться

2 ответа

Я считаю, что GCC не соответствует требованиям. N3092 §5.1.2 / 5 говорит

Тип закрытия для лямбда-выражения имеет общедоступный встроенный оператор вызова функции (13.5.4), чей параметры и возвращаемый тип описываются лямбда-выражением параметром-объявлением-предложением и конечным-возвращаемым-типом соответственно. Этот оператор вызова функции является объявленным константой (9.3.1) тогда и только тогда, когда в предложении-объявлении-параметра лямбда-выражения не является , за которым следует изменяемый.

Таким образом, хотя многое в типе закрывающего объекта определяется реализацией, сама функция должна быть членом, чтобы быть общедоступным , и должна быть нестатическим членом, чтобы быть 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;
}

output:

member of Z4mainEUlvE_, returns i takes i

EDIT: Похоже, единственная проблема заключается в том, что указатели на определенные операторы вызова закрытия не соответствуют шаблонам шаблонов ptmf. Обходной путь - объявить лямбда-выражение изменяемым . Это бессмысленно, если нет захвата и только (помимо устранения проблемы), кажется, изменяется постоянство оператора вызова.

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 

Без изменяемого и с константой spectfun не печатает подписи ни для одного из последних двух запросов.

4
ответ дан 1 December 2019 в 05:19
поделиться

Прочтите n3043 . Лямбды теперь можно преобразовать в указатели на функции, если у них нет состояния. Я полагаю (... но не знаю) GCC изначально реализовал это поведение случайно, "исправил его", теперь будет повторно добавлять его в 4.5 или 4.6. VC10 правильно реализовал лямбда-выражения, как это было изначально спроектировано, но не соответствовал последним рабочим документам с n3043.

1
ответ дан 1 December 2019 в 05:19
поделиться
Другие вопросы по тегам:

Похожие вопросы: