Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

std::for_each ignoring default function argument

Tags:

c++

std

boost

I stumbled on a strange compilation problem. I want to process a list of strings, using std::for_each. The following simplified code illustrates the problem :

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

I tested using MinGW 4.5.2 and MSVC10, both reported the same error message. Originally, I wanted to use boost::algorithm::trim as a processing function passed to std::for_each, but I found that it takes two arguments, the first being mandatory (the string to process) and the second one is optional (a locale providing a definition for space chars).

Is there any way to keep things clean when using std::for_each(and other standard algorithms) when having functions or methods with default arguments ? I found a way to make it work, but it is no more clear and easily understandable, so a for loop begins to seem easier ...

# 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

Thanks for any help !

Note : I just started learning English, sorry for mistakes :)

like image 542
overcoder Avatar asked Jul 04 '11 11:07

overcoder


1 Answers

Default arguments are just a code-generation tool and not part of the function signature, so you can't really get around that. You could wrap your function in a function object, but that's precisely what bind already does for you.

However, in C++0x you can conveniently store the result (and use std::bind) to maybe make the code a bit more readable:

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

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

(The reason you don't want to do that in C++98/03 is that the return type of bind is something rather unsightly, and you wouldn't be doing anyone a favour by spelling it out.)

Alternatively, again in C++0x, you could use a lambda:

std::for_each(text.begin(), text.end(), [](std::wstring & s){ boost::algorithm::trim<std::wstring>(s); });
like image 126
Kerrek SB Avatar answered Oct 07 '22 19:10

Kerrek SB