2012-10-22 28 views
8

我試圖使用C++ 11功能來使自定義流操作器更容易創建。我可以使用lambda函數作爲操縱器,但不能使用std::function<ostream&(ostream&)>std ::作爲自定義流操縱器的函數

下面的代碼,歸結:

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

第二cout語句失敗,出現以下:

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>&)>]' 

爲什麼會失敗?我正在使用cygwin gcc 4.5.3。

雖然我在問,但我並不是因爲效率問題而到處使用std::function。但我希望編寫返回lambda函數的函數,並且不知道如何在沒有std::function的情況下這樣做。例如,類似以下內容會很好

auto getAdditionFunctor(); 

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

......但顯然不起作用。有沒有可用的替代語法?我無法想象它會是什麼,所以我可能會被困在std::function

如果我有第二個問題的解決方案,那麼第一個問題是沒有意義的。


謝謝。

定義operator<<(ostream&, std::function<ostream&(ostream&)>幫助。我誤解了一個網頁,並且有一種印象,ostream足夠聰明,可以將一個任意物體作爲一個操縱器來對待。我錯了。此外,我建立的簡單lambda可能只是被編譯成一個普通的舊函數,就像我被告知。事實上,如果我使用變量捕獲來確保lambda不是一個簡單的函數,那麼編譯器會失敗。此外,與operator()對象定義的不是(默認)爲機械手處理:

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 
} 

此外更新:事實證明比下面的那些稍微更健壯的解決方案可以用來完成:

// 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); 
} 

此代碼有一個額外的const。我發現這個試圖在我的項目中實際實現解決方案。

+4

你是不是想在這些操縱器中留下'回報'? –

+0

我讀過那些暗示,並添加它們沒有幫助。在這種情況下,我認爲他們對回報更具可讀性,但他們沒有合法合法性。畢竟,如果我註釋掉'cout << functionManip;',所有東西都可以正常工作。 –

+3

@EdKrohne:'return'在這裏不是可選的 - 你的lambdas通過擁有一個非'void'返回類型來調用UB,並且不像任何其他函數那樣返回一個值。 §6.6.3/ 2:「*從一個函數的末尾流出,相當於一個沒有值的'return';這會導致在一個返回值函數中出現未定義的行爲。*」看起來工作正常只是一種可能的表現形式的UB。 – ildjarn

回答

7

如果你看一下operator<<ostream,有用於拍攝std::function無過載 - 而這基本上你想與cout << functionManip這裏做什麼。爲了解決這個問題,無論是自己定義的過載:

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

或者通過stream作爲參數傳遞給函數:

functionManip(std::cout); 

至於爲什麼lambda工作,考慮到的返回類型lambda是不確定的,並且有一個過載使用函數指針:

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

拉姆達可能是包裝的一切utili zing一個結構,並定義operator(),在這種情況下,它將完全像函數指針一樣工作。這對我來說是最可能的解釋,希望有人能糾正我,如果我錯了。

+1

行爲不是偶然的,它是由標準強制的。不捕獲任何東西的lambda是* always *可隱式轉換爲普通的舊函數指針。 (反過來說:一個lambda表示捕獲任何東西都不會被隱式轉換爲函數指針。) – Quuxplusone

5

這不是錯誤的原因,但既然你已經定義了兩個lambdaManipfunctionManip爲具有返回類型ostream&我相信你忘了添加return stream;兩者。


調用cout << functionManip失敗,是因爲沒有定義operator<<(ostream&, std::function<ostream&(ostream&)>。添加一個,通話將成功。

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

或者,您可以撥打functionManip

functionManip(cout); 

這將不添加operator<<定義工作。


至於你有關返回拉姆達,由於getAdditionFunctor返回的拉姆達是捕捉少拉姆達的問題,它可以被隱式轉換爲一個函數指針。

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

好點,我應該考慮一下我的玩具例子。但是如果我捕獲了呢?變量捕獲是從函數返回一個'lambda'的點;如果我不想進行變量捕獲,那麼首先使用普通的舊函數。 –

+0

+1。我不知道無捕獲的lambda可以隱式轉換爲函數指針,但它非常有意義。 – Yuushi

3

前面的回答得到了藝術右側的狀態,但如果你打算使用std::function S和/或lambda表達式在你的代碼流操縱嚴重,那麼你可能只是想添加下面的函數模板定義你的代碼庫,在全局範圍內:

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

有使用expression SFINAE,所以這個特殊的operator<<超載甚至不會參與重載決議,除非m(os)是一個結構良好的表達尾隨返回類型。 (這是你想要的。)

然後,你甚至可以做這樣的事情

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

(注意,完全沒有在上面的代碼中的任何std::function對象!儘量避免餡lambda表達式爲std::function對象,除非你絕對有必要,因爲構建std::function對象可以是昂貴的,它甚至可以涉及到內存分配)

這將是有意義的C++ 17這個operator<<超載添加到標準庫,但我不知道。在這一領域的任何具體建議NT。