6

Is it possible to use a template type argument from a surrounding template function within a local anonymous function? I'm pretty certain I can't declare a template lambda ...

For example how would I go about doing something like this:

template <typename T>
void TrimString(std::basic_string<T>& str, const std::locale& loc = std::locale())
{
    // std::isspace as lambda unary predicate?
    auto fn = [&loc](T c){ return std::use_facet<std::ctype<T>>(loc).is(std::ctype_base::space, c); };
    // trim right
    str.erase(std::find_if(str.rbegin(), str.rend(), std::not1(fn)).base(), str.end());
    // trim left
    str.erase(str.begin(), std::find_if(str.begin(), str.end(), std::not1(fn)));
}

Currently this generates the following error:

error C2039: 'argument_type' : is not a member of '`anonymous-namespace'::<lambda0>'

Which makes sense as the lambda has no clue about argument T from the surrounding template function.

I use VS2010 and gcc 4.7 but I don't want to use boost.

Any ideas?

Edit: It appears I was wrong in my assumption that the problem was the template argument itself. Rather it is the use of std::not1 being compiled with the lambda function. Here is the more verbose error output:

error C2039: 'argument_type' : is not a member of '`anonymous-namespace'::<lambda0>'
 : see declaration of '`anonymous-namespace'::<lambda0>'
 : see reference to class template instantiation 'std::unary_negate<_Fn1>' being compiled
          with
          [
              _Fn1=`anonymous-namespace'::<lambda0>
          ]
 : see reference to function template instantiation 'void TrimString<char>(std::basic_string<_Elem,_Traits,_Ax> &,const std::locale &)' being compiled
          with
          [
              _Elem=char,
              _Traits=std::char_traits<char>,
              _Ax=std::allocator<char>
          ]

Do you need to explicitly declare the type for the argument if it's a function type? I'm not sure what I'm doing wrong still ...

Answers:

Option 1: If I don't use std::not1 and instead negate the returned value in the lambda I get the same behavior without issue.

auto fn = [&loc](T c){ return !std::use_facet<std::ctype<T>>(loc).is(std::ctype_base::space, c); };

Option 2: Since the lambda is not longer equivalent to how std::isspace would behave as a unary predicate a function object constructor cast also does the trick.

str.erase(std::find_if(str.rbegin(), str.rend(), std::not1(std::function<bool(T)>(fn))).base(), str.end());
2
  • BTW, the error message seems to indicate that the problem lies somewhere else, specifically with the lambda declared at namespace scope. Commented Jun 21, 2012 at 1:23
  • 1
    Casting fn like this std::not1(std::function<bool(char)>(fn)) also works. Commented Jun 21, 2012 at 1:58

3 Answers 3

9

The problem is not caused by using a template parameter inside the lambda, as the parameter has already been resolved to a type at the time when the lambda is constructed.

The problem is that the lambda you define cannot be combined with std::not1, which requires, as argument, a std::unary_function<argument_type,return_type>.

The easiest way to solve the problem is to not use std::not1, and instead negate the predication right in the lambda expression:

auto fn = [&loc](T c){ return !std::use_facet<std::ctype<T>>(loc).is(std::ctype_base::space,c); };

The complete code that compiles and works with GCC 4.7.0 then becomes:

#include <string>
#include <algorithm>
#include <locale>
#include <iostream>

template <typename T>
void TrimString(std::basic_string<T>& str, const std::locale& loc = std::locale())
{
  auto fn = [&loc](T c){ return !std::use_facet<std::ctype<T>>(loc).is(std::ctype_base::space,c); };

  str.erase(std::find_if(str.rbegin(), str.rend(),fn).base(), str.end());
  str.erase(str.begin(), std::find_if(str.begin(), str.end(), fn));
}

int main() {
  std::basic_string<char> s("  hello  ");
  TrimString(s);
  std::cout << s << std::endl;
  return 0;
}

This outputs

hello

as expected.

Sign up to request clarification or add additional context in comments.

1 Comment

Yup, just came to this conclusion myself now that I was looking at the actual problem!
6

You most certainly can use T as parameter type of a lambda expression. The following program compile fines on GCC 4.5.1 :

include <iostream>

template<typename T>
void f(T arg)
{
   auto print = [](T a) { std::cout << a << std::endl; };
   print(arg);
}

int main() {
        f(8899);
        f("Nawaz");
        return 0;
}

See yourself : http://ideone.com/l32Z6

BTW, the error message seems to indicate that the problem lies somewhere else, specifically with a lambda declared at namespace scope:

error C2039: 'argument_type' : is not a member of '`anonymous-namespace'::<lambda0>'


After your EDIT, all I can say is that dont use std::not1 then. In fact, you don't even need it. You could use return !whatever-expression in the lambda itself.

2 Comments

@Jason: That is what it seems to be.
Yes indeed that's exactly what I did and it seems to be working after a few overloads to help the compiler with literal const char[] usage. Thanks for pointing out the real problem!
2

Edit: As @Nawaz points out, your error must be coming form somewhere else ... what I describe below is overkill ...

Using decltype, you could do something like the following:

template <typename T>
void TrimString(std::basic_string<T>& str, 
                const std::locale& loc = std::locale(), 
                T arg = T())
{
    auto fn = [&loc](decltype(arg) c){ return std::use_facet<std::ctype<T>>(loc).is(std::ctype_base::space, c); };

    //...rest of your code
}

This is using (or abusing) the fact that the expression decltype(arg) evaluates to the type of arg, which in this case is the type of T.

4 Comments

Why use decltype when T should work just fine? He must be doing something else also.
I was assuming his error was coming from what he said it was, that is the use of T rather than a known argument type ... obviously your post shows that's there's no issue with that ...
If I capture arg and use decltype I get the same error. Obviously if I don't capture it then it complains about implict capture or no default capture mode. This might be along the right lines even though it doesn't work ... or MSVC is broken if this should work.
This compiled and worked for me under GCC ... yes, maybe this is a VC10 problem ...

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.