2011-07-04 16 views
7

Natknąłem się na dziwny problem kompilacji. Chcę przetworzyć listę ciągów, używając std::for_each. Poniższy kod ilustruje uproszczony problem:std :: for_each ignorowanie argumentu funkcji domyślnej

# include <list> 
# include <string> 
# include <algorithm> 

using namespace std ; 

void f(wstring & str) 
{ 
    // process str here 
} 

void g(wstring & str, int dummy = 0) 
{ 
    // process str here, same as f, just added a second default dummy argument 
} 

int main(int, char*[]) 
{ 
    list<wstring> text ; 

    text.push_back(L"foo") ; 
    text.push_back(L"bar") ; 

    for_each(text.begin(), text.end(), f) ; // OK, fine :) 
    for_each(text.begin(), text.end(), g) ; // Compilation error, complains about 
        // g taking 2 arguments, but called within std::for_each 
        // with only one argument. 

    // ... 
    return 0 ; 
}  

testowałem przy użyciu MinGW 4.5.2 i MSVC10, zarówno zgłoszone ten sam komunikat o błędzie. Pierwotnie chciałem użyć boost::algorithm::trim jako funkcji przetwarzania przekazanej do std::for_each, ale odkryłem, że wymaga ona dwóch argumentów, z których pierwszy jest obowiązkowy (ciąg do przetworzenia), a drugi jest opcjonalny (ustawienie regionalne określające definicję znaków spars) .

Czy istnieje sposób na zachowanie czystości podczas korzystania z funkcji std::for_each (i innych standardowych algorytmów) w przypadku funkcji lub metod z domyślnymi argumentami? Znalazłem sposób, aby to działało, ale to nie jest bardziej jasne i zrozumiałe, a więc for pętla zaczyna wydawać się łatwiejsze ...

# include <list>  
# include <string> 
# include <algorithm> 
# include <boost/bind.hpp> 
# include <boost/algorithm/string.hpp> 

using namespace std ; 
using namespace boost ; 

// ... somewhere inside main 
list<wstring> text ; 
for_each(text.begin(), text.end(), bind(algorithm::trim<wstring>, _1, locale()) ; 
// One must deal with default arguments ... 
// for_each(text.begin(), text.end(), algorithm::trim<wstring>) would be a real pleasure 

dzięki za pomoc!

Uwaga: Właśnie rozpoczął naukę angielskiego, przepraszam za błędy :)

+0

Myślę, że po prostu nie działa w ten sposób dla funktorów. Usuń atrapę z argumentu. –

+0

Argumentem sztucznym dodanym do 'g' było zademonstrowanie dziwnego zachowania' std :: for_each'. Wywołanie 'g' z jakimś' wstring str' jak 'g (str)' działa jak znak uroku, ale nie wewnątrz 'std :: for_each'. W moim przypadku użycie 'boost :: algorithm :: trim' bezpośrednio jako funktora jest niemożliwe bez lew, ponieważ jego drugi argument jest opcjonalny (std :: locale) – overcoder

+2

@ Overwerk: to dlatego, że' g (str) 'natychmiast staje się 'g (str, 0)' - domyślnym argumentem jest po prostu cukier generujący kod. –

Odpowiedz

6

argumenty domyślne są tylko narzędziem kod generacji i nie jest częścią podpisu funkcji, tak naprawdę nie można obejść to. Możesz zawinąć swoją funkcję w obiekcie funkcji, ale to właśnie zrobiła już dla ciebie bind.

Jednak w C++ 0x można wygodnie przechowywać wynik (i używać std::bind) może wprowadzać do kodu nieco bardziej czytelny:

auto trimmer = std::bind(boost::algorithm::trim<std::wstring>, std::placeholders::_1, std::locale()); 

std::for_each(text.begin(), text.end(), trimmer); 

(Powodem nie chcesz tego robić w C++ 98/03 jest to, że zwracany typ bind jest czymś raczej brzydkim, a ty nie robiłbyś nikomu przysługi poprzez pisownię.)

Alternatywnie, znowu w C++ 0x, możesz użyj wartości lambda:

std::for_each(text.begin(), text.end(), [](std::wstring & s){ boost::algorithm::trim<std::wstring>(s); }); 
+2

Ponadto, jeśli utknąłeś z aktualnym standardem, spójrz na bind2nd (w rzeczywistości ma pewne wymagania dla funktorów - twój g powinien być strukturowany dziedzicząc z funkcji binary) – cybevnm

+0

@vnm: tak, dobry punkt, jedna zależność mniej doładowania: ' std :: for_each (..., ..., std :: bind2nd (boost :: algorytm :: trim , std :: locale())); '. –

+1

Dzięki! Zobaczę, czy mogę włączyć C++ 0x dla mojego bieżącego projektu. Lambda wydaje się wygodniejsza. – overcoder

Powiązane problemy