2012-10-22 25 views
8

Tôi đang cố gắng sử dụng các tính năng C++ 11 để tạo các trình xử lý luồng tùy chỉnh dễ dàng hơn để tạo. Tôi có thể sử dụng hàm lambda làm thao tác, nhưng không sử dụng std::function<ostream&(ostream&)>.std :: chức năng như một trình xử lý dòng tùy chỉnh

Dưới đây là đoạn code, luộc xuống:

#include <iostream> 
#include <functional> 
using namespace std; 

auto lambdaManip = [] (ostream& stream) -> ostream& { 
    stream << "Hello world" << endl; 
}; 
function<ostream& (ostream&)> functionManip = [] (ostream& stream) -> ostream& { 
    stream << "Hello world" << endl; 
}; 

int main (int argc, char** argv) { 
    cout << lambdaManip; // OK 
    cout << functionManip; // Compiler error 
} 

Thứ hai cout tuyên bố không thành công với những điều sau đây:

g++-4 src/Solve.cpp -c -g -std=c++0x -o src/Solve.o -I/home/ekrohne/minisat 
src/Solve.cpp: In function 'int main(int, char**)': 
src/Solve.cpp:24:11: error: cannot bind 'std::ostream' lvalue to 'std::basic_ostream<char>&&' 
/usr/lib/gcc/i686-pc-cygwin/4.5.3/include/c++/ostream:579:5: error: initializing argument 1 of 'std::basic_ostream<_CharT, _Traits>& std::operator<<(std::basic_ostream<_CharT, _Traits>&&, const _Tp&) [with _CharT = char, _Traits = std::char_traits<char>, _Tp = std::function<std::basic_ostream<char>&(std::basic_ostream<char>&)>]' 

Tại sao điều này không? Tôi đang sử dụng Cygwin gcc 4.5.3.

Trong khi tôi hỏi, tôi không phải lo về việc sử dụng std::function ở mọi nơi, do các vấn đề về hiệu quả. Nhưng tôi muốn viết các hàm trả về các hàm lambda, và không biết làm thế nào để làm như vậy mà không có std::function. Ví dụ: một số nội dung như sau sẽ tuyệt vời

auto getAdditionFunctor(); 

auto getAdditionFunctor() { 
    return [] (int x, int y) { return x + y }; 
}; 

... nhưng rõ ràng là không hoạt động. Có cú pháp thay thế nào hoạt động không? Tôi không thể tưởng tượng nó có thể là gì, vì vậy tôi có thể bị mắc kẹt với std::function.

Nếu tôi có giải pháp cho câu hỏi thứ hai, thì câu hỏi đầu tiên sẽ là tranh luận.


Cảm ơn bạn.

Xác định operator<<(ostream&, std::function<ostream&(ostream&)> đã giúp. Tôi đã đọc sai một trang web và theo ấn tượng rằng ostream đủ thông minh để xử lý một đối tượng tùy ý có số operator() làm người thao túng. Tôi đã sai về điều này. Hơn nữa, đơn giản lambda Tôi xây dựng có lẽ chỉ được biên dịch thành một chức năng cũ đơn giản, giống như tôi đã nói. Thật vậy, nếu tôi sử dụng chụp biến để đảm bảo rằng lambda không phải là một chức năng đơn giản, thì trình biên dịch không thành công. Ngoài ra, các đối tượng với operator() định nghĩa là không (theo mặc định) được coi là thao tác:

class Manipulator { 
    ostream& operator()(ostream& stream) const { 
     return stream << "Hello world" << endl; 
    }; 
} classManip; 

function<ostream& (ostream&)> functionManip = [] (ostream& stream) -> ostream& { 
    return stream << "Hello world" << endl; 
}; 

int main (int argc, char** argv) { 
    const string str = "Hello world"; 
    auto lambdaManip = [&] (ostream& stream) -> ostream& { 
     return stream << str << endl;  
    }; 

    cout << classManip;  // Compiler error 
    cout << lambdaManip; // Compiler error 
    cout << functionManip; // Compiler error 
} 

cập nhật thêm: hóa ra một giải pháp nhẹ mạnh mẽ hơn những người dưới đây có thể được thực hiện với:

// Tell ostreams to interpret std::function as a 
// manipulator, wherever it sees one. 
inline ostream& operator<<(
     ostream& stream, 
     const function<ostream& (ostream&)>& manipulator) { 
    return manipulator(stream); 
} 

Mã này có thêm const. Tôi phát hiện ra điều này đang cố gắng thực sự triển khai giải pháp trong dự án của tôi.

+4

Bạn có ý định bỏ qua 'trở lại' trong các thao tác đó không? –

+0

Tôi đã đọc rằng những điều đó được ngụ ý và việc thêm chúng không giúp ích gì. Trong trường hợp này, tôi nghĩ rằng chúng dễ đọc hơn với lợi nhuận, nhưng chúng không hợp pháp. Sau khi tất cả, tất cả mọi thứ hoạt động tốt nếu tôi nhận xét 'cout << functionManip;'. –

+3

@EdKrohne: 'return' là ** không ** tùy chọn ở đây - lambdas của bạn gọi UB bằng cách không có kiểu trả về' không 'và không trả về một giá trị, giống như với bất kỳ hàm nào khác. §6.6.3/2: "* Chảy ra khỏi đầu của một hàm tương đương với' return' không có giá trị; kết quả là hành vi không xác định trong hàm trả về. * "Xuất hiện để làm việc tốt chỉ là một biểu hiện có thể của UB. – ildjarn

Trả lời

7

Nếu bạn xem operator<< cho ostream, không có quá tải khi chụp std::function - và đó là những gì bạn đang cố gắng làm ở đây với cout << functionManip.Để sửa lỗi này, hoặc là xác định tình trạng quá tải cho mình:

ostream& operator<<(ostream& os, std::function<ostream& (ostream&)>& s) 
{ 
    return s(os); 
} 

Hoặc vượt qua stream như một cuộc tranh cãi với chức năng:

functionManip(std::cout); 

Đối với lý do tại sao lambda đang làm việc, cho rằng các kiểu trả về của một lambda không được xác định và có quá tải để sử dụng con trỏ hàm:

ostream& operator<< (ostream& (*pf)(ostream&)); 

Chữ lambda có lẽ là tất cả mọi thứ utili zing một cấu trúc và xác định operator() mà trong trường hợp này sẽ hoạt động chính xác như một con trỏ hàm. Điều này với tôi là giải thích có khả năng nhất, hy vọng ai đó có thể sửa tôi nếu tôi sai.

+1

Hành vi không phải là ngẫu nhiên, nó được bắt buộc bởi Tiêu chuẩn. Một lambda mà không nắm bắt bất cứ điều gì là * luôn luôn * ngầm chuyển đổi sang một con trỏ chức năng cũ đồng bằng. (Và ngược lại: một lambda mà * không * chụp bất cứ thứ gì là * không bao giờ * chuyển đổi ngầm thành con trỏ hàm.) – Quuxplusone

5

Đây không phải là nguyên nhân của lỗi, nhưng vì bạn đã xác định cả hai lambdaManipfunctionManip khi có loại trả lại ostream& Tôi tin rằng bạn đã quên thêm return stream; cho cả hai.


Cuộc gọi cout << functionManip thất bại vì không có operator<<(ostream&, std::function<ostream&(ostream&)> xác định. Thêm một và cuộc gọi sẽ thành công.

ostream& operator<<(ostream& stream, function<ostream& (ostream&)>& func) { 
    return func(stream); 
} 

Ngoài ra, bạn có thể gọi functionManip như

functionManip(cout); 

này sẽ làm việc mà không cần thêm định nghĩa operator<<.


Đối với câu hỏi của bạn về việc trả lại một lambda, kể từ khi lambda được trả về bởi getAdditionFunctor là một lambda chụp-ít hơn, nó có thể được ngầm chuyển đổi sang một con trỏ hàm.

typedef int(*addition_ptr)(int,int); 
addition_ptr getAdditionFunctor() 
{ 
    return [] (int x, int y) -> int { return x + y; }; 
} 

auto adder = getAdditionFunctor(); 
adder(10,20); // Outputs 30 
+0

Điểm tốt, tôi nên nghĩ về điều đó cho ví dụ đồ chơi của mình. Nhưng nếu tôi đang chụp? Biến biến là điểm trả về một 'lambda' từ một hàm; nếu tôi không muốn chụp ảnh thay đổi, tôi chỉ sử dụng chức năng cũ đơn giản ngay từ đầu. –

+0

+1. Tôi không biết lambdas chụp ít được chuyển đổi hoàn toàn thành con trỏ hàm, nhưng nó có ý nghĩa hoàn hảo. – Yuushi

3

Những câu trả lời trước đó có tình trạng quyền nghệ thuật, nhưng nếu bạn đang có được nặng nề sử dụng std::function s và/hoặc lambdas như thao tác dòng trong mã của bạn, sau đó bạn chỉ có thể muốn thêm sau hàm mẫu định nghĩa để codebase của bạn, ở phạm vi toàn cầu:

template<class M> 
auto operator<< (std::ostream& os, const M& m) -> decltype(m(os)) 
{ 
    return m(os); 
} 

Các loại dấu sự trở lại có sử dụng expression SFINAE, vì vậy đặc biệt operator<< quá tải này thậm chí sẽ không được tham gia giải quyết tình trạng quá tải trừ khi m(os) là một biểu hiện tốt được hình thành . (Đây là những gì bạn muốn.)

Sau đó, bạn thậm chí có thể làm những việc như

template<class T> 
auto commaize(const T& t) 
{ 
    return [&t](std::ostream& os) -> std::ostream& { 
     return os << t << ", "; 
    }; 
} 

int main() 
{ 
    std::cout << commaize("hello") << commaize("darling") << std::endl; 
} 

(Chú ý tổng thiếu của bất kỳ std::function đối tượng trong đoạn code trên! Cố gắng tránh lambdas nhồi nhét vào std::function đối tượng, trừ khi bạn hoàn toàn phải, bởi vì việc xây dựng một đối tượng std::function có thể tốn kém, nó thậm chí có thể liên quan đến việc cấp phát bộ nhớ.)

Sẽ có ý nghĩa khi C++ 17 thêm quá tải này vào thư viện chuẩn, nhưng tôi không biết của bất kỳ đề xuất cụ thể nào trong khu vực đó tại mome nt.

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