Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use template type argument in lambda?

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());
like image 985
AJG85 Avatar asked Jun 21 '12 00:06

AJG85


People also ask

Can you template a lambda?

Lambda-expressions are not allowed in unevaluated expressions, template arguments, alias declarations, typedef declarations, and anywhere in a function (or function template) declaration except the function body and the function's default arguments.

What are template arguments?

In C++ this can be achieved using template parameters. A template parameter is a special kind of parameter that can be used to pass a type as argument: just like regular function parameters can be used to pass values to a function, template parameters allow to pass also types to a function.

Can we pass Nontype parameters to templates?

Template classes and functions can make use of another kind of template parameter known as a non-type parameter. A template non-type parameter is a template parameter where the type of the parameter is predefined and is substituted for a constexpr value passed in as an argument.

Are functors the same as lambdas?

Lambdas are just regular C++ classes. They make use of the operator() member function. These are actually called “functors” in C++ There is no extra overhead or performance penalty for using them.


2 Answers

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.

like image 104
jogojapan Avatar answered Sep 19 '22 13:09

jogojapan


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.

like image 35
Nawaz Avatar answered Sep 19 '22 13:09

Nawaz