【问题标题】:How to use template type argument in lambda?如何在 lambda 中使用模板类型参数?
【发布时间】:2012-06-21 00:48:22
【问题描述】:

是否可以在本地匿名函数中使用来自周围模板函数的模板类型参数?我很确定我不能声明模板 lambda ...

例如,我将如何做这样的事情:

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

目前这会产生以下错误:

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

这是有道理的,因为 lambda 不知道来自周围模板函数的参数 T

我使用 VS2010 和 gcc 4.7,但我不想使用 boost。

有什么想法吗?

编辑:我认为问题出在模板参数本身的假设似乎是错误的。而是使用 lambda 函数编译 std::not1。这是更详细的错误输出:

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>
          ]

如果是函数类型,是否需要显式声明参数的类型?我不确定我做错了什么......

答案:

选项 1:如果我不使用 std::not1 而是否定 lambda 中的返回值,我会得到相同的行为而不会出现问题。

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

选项 2:由于 lambda 不再等同于 std::isspace 作为一元谓词的行为方式,因此函数对象构造函数强制转换也可以解决问题。

str.erase(std::find_if(str.rbegin(), str.rend(), std::not1(std::function<bool(T)>(fn))).base(), str.end());

【问题讨论】:

  • 顺便说一句,错误消息似乎表明问题出在其他地方,特别是在 namespace 范围内声明的 lambda。
  • 像这样std::not1(std::function&lt;bool(char)&gt;(fn)) 投射fn 也可以。

标签: c++ templates lambda c++11


【解决方案1】:

问题不是在 lambda 中使用模板参数引起的,因为该参数在构造 lambda 时已经解析为类型。

问题是您定义的 lambda 不能与 std::not1 组合,后者需要 std::unary_function&lt;argument_type,return_type&gt; 作为参数。

解决问题的最简单方法是不使用std::not1,而是在lambda表达式中否定谓词:

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

编译和使用 GCC 4.7.0 的完整代码变成:

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

这个输出

hello

正如预期的那样。

【讨论】:

  • 是的,现在我正在查看实际问题,我自己才得出这个结论!
【解决方案2】:

您当然可以使用T 作为 lambda 表达式的参数类型。以下程序在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;
}

看看你自己:http://ideone.com/l32Z6

顺便说一句,错误消息似乎表明问题出在其他地方,特别是在 namespace 范围内声明的 lambda:

错误 C2039:“argument_type”:不是 '`anonymous-namespace'::'

的成员

在您编辑之后,我只能说不要使用 std::not1。事实上,你甚至不需要它。您可以在 lambda 本身中使用 return !whatever-expression

【讨论】:

  • @Jason:看起来就是这样。
  • 是的,这正是我所做的,并且在几次重载后似乎可以正常工作,以帮助编译器使用文字 const char[]。感谢您指出真正的问题!
【解决方案3】:

编辑:正如@Nawaz 指出的那样,您的错误一定来自其他地方......我在下面描述的内容有点矫枉过正......

使用decltype,您可以执行以下操作:

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
}

这是在使用(或滥用)表达式decltype(arg) 的计算结果为arg 的类型,在本例中为T 的类型。

【讨论】:

  • T 应该可以正常工作时,为什么还要使用decltype?他一定也在做其他事情。
  • 我假设他的错误来自他所说的,即使用 T 而不是已知的参数类型......显然你的帖子表明这没有问题。 ..
  • 如果我捕获arg 并使用decltype 我会得到同样的错误。显然,如果我不捕获它,那么它会抱怨隐含捕获或没有默认捕获模式。这可能是正确的,即使它不起作用......或者如果这应该起作用,MSVC 就会被破坏。
  • 这个在 GCC 下编译并为我工作......是的,也许这是一个 VC10 问题......
猜你喜欢
  • 2013-01-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-12-01
  • 2017-02-09
  • 1970-01-01
  • 2012-06-18
  • 1970-01-01
相关资源
最近更新 更多