2010-04-09 33 views
15

Tôi quan sát một số mâu thuẫn giữa hai trình biên dịch (g ++ 4.5, VS2010 RC) theo cách chúng khớp với lambdas với các chuyên môn từng phần của các mẫu lớp. Tôi đã cố gắng thực hiện một cái gì đó như boost :: function_types cho lambdas để trích xuất các đặc điểm kiểu. Kiểm tra this để biết thêm chi tiết.các đặc tính lambda không đồng nhất giữa các trình biên dịch C++ 0x

Trong g ++ 4.5, loại operator() của lambda có vẻ giống như chức năng đứng tự do (R (*) (...)) trong khi trong VS2010 RC, nó có vẻ giống như hàm thành viên (R (C :: *) (...)). Vì vậy, câu hỏi là các nhà văn biên dịch tự do để giải thích bất kỳ cách nào họ muốn? Nếu không, trình biên dịch nào là chính xác? Xem chi tiết bên dưới.

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

Chương trình này biên dịch trên cả g ++ 4.5 và VS2010 nhưng hàm_tác năng được khởi tạo khác với ghi chú trong mã.

Trả lời

4

Tôi tin rằng GCC không tuân thủ. N3092 §5.1.2/5 nói

Kiểu đóng cửa cho một lambda-biểu có inline hàm operator gọi công cộng (13.5.4) có eters param- và kiểu trả về là mô tả bằng mệnh đề khai báo tham số của lambda-expression và kiểu trả về theo sau tương ứng. Toán tử gọi hàm này là được khai báo const (9.3.1) nếu và chỉ khi mệnh đề khai báo tham số của lambda- expression không phải là tiếp theo là có thể thay đổi.

Vì vậy, trong khi nhiều điều về loại đối tượng đóng của là thực hiện xác định, chức năng riêng của mình phải là thành viên để được public và phải là thành viên không tĩnh được const.

EDIT: Chương trình này chỉ ra rằng operator() là một chức năng thành viên trên GCC 4.6, cơ bản giống như 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; 
} 

đầu ra:

member of Z4mainEUlvE_, returns i takes i 

EDIT: Dường như vấn đề duy nhất là con trỏ để khai thác cuộc gọi đóng cửa nhất định không phù hợp với mô hình mẫu ptmf. Cách giải quyết khác là khai báo biểu thức lambda mutable. Điều này là vô nghĩa nếu không có nắm bắt và chỉ (ngoài việc sửa chữa vấn đề) dường như thay đổi const-ness của nhà điều hành cuộc gọi.

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; 

đầu ra:

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

Nếu không có thể thay đổi và với const, spectfun không in chữ ký cho một trong hai câu hỏi cuối cùng.

+1

Điều đó không làm cho g ++ trở nên không tương thích, vì nó sử dụng chức năng miễn phí? – GManNickG

+0

vâng, âm thanh với tôi như nó là GCC đó là nhận được nó sai. – jalf

+0

@Gman, jalf: Bah, nghe có chọn lọc. Vâng, phương pháp của GCC liên quan đến tình bạn nhiều hơn ... ha! – Potatoswatter

1

Đọc n3043. Lambdas giờ đây có thể chuyển đổi thành con trỏ hàm, miễn là chúng không có trạng thái nào. Tôi tin rằng (... nhưng không biết) GCC ban đầu thực hiện hành vi này vô tình, "cố định nó", bây giờ sẽ được tái bổ sung nó vào 4,5 hoặc 4,6.VC10 thực hiện lambdas đúng như thiết kế ban đầu, nhưng không phù hợp với các giấy tờ làm việc mới nhất với n3043.

+0

n3092 là bản nháp mới nhất: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2010/n3092.pdf [Liên kết PDF] – GManNickG

+0

Có, đó là - nhưng tôi đang nói về n3043 cụ thể. –

+0

Mặc dù lambdas không trạng thái có thể chuyển đổi thành con trỏ hàm, khi một kết hợp tốt hơn R (C :: *) (...) có mặt, nó sẽ được chọn, phải không? – Sumant

0

Tôi nghĩ các nhà phát triển gcc có lý do chính đáng cho hành vi này. Hãy nhớ rằng, một hàm tĩnh không có con trỏ "this", và khi nó đang được gọi, người gọi không cần phải chuyển con trỏ "this". Vì vậy, đây là một tối ưu hóa hiệu suất nhỏ khi nó thực sự không có gì chứa trong đối tượng đóng cửa. Và bạn có thể thấy nhà phát triển G ++ để lại cho bạn cách giải quyết bằng cách tuyên bố biểu thức lambda là "có thể thay đổi" (nhớ bạn thực sự không có bất cứ điều gì để thay đổi).

Các vấn đề liên quan