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());
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.
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.
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.
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.
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With