lambda(参数)中的 const 限定符,用于带有 std::sregex_token_iterator 的 std::transform

Posted

技术标签:

【中文标题】lambda(参数)中的 const 限定符,用于带有 std::sregex_token_iterator 的 std::transform【英文标题】:const qualifier in lambda(parameter) for std::transform with std::sregex_token_iterator 【发布时间】:2019-07-23 10:27:48 【问题描述】:

我回答了关于 SO (this) 的问题,以解析一些字符串并产生特定的输出。为此,我使用了std::transform。对于unary_op,我使用了 lambda。 lambda 的参数显然需要为const。强迫我引入一个额外的局部变量。如果不使用const,我会收到语法错误(编译器:MS VS19)。

我检查了 cppreference here 上的 std::transorm 文档。

unary_op - 将应用的一元操作函数对象。

函数的签名应该等同于:

Ret fun(const Type &a);

签名不需要有 const &。 类型 Type 必须使得 InputIt 类型的对象可以被取消引用,然后隐式转换为 Type。 Ret 类型必须使得 OutputIt 类型的对象可以被取消引用并分配一个 Ret 类型的值。 ​

我不完全理解这一点。我是否需要使用const& 参数。这是std::sregex_token_iterator 实施的结果吗?我试图了解std::regex_token_iterator 的文档。抱歉,我不明白取消引用此迭代器是否可以转换为 std::string。

.

我在这里走错了吗?或者有人可以解释一下为什么它会这样吗?或者有没有我看不到的解决方案?

.

示例代码:

#include <string>
#include <iostream>
#include <regex>
#include <iterator>

int main()

    // 1. This is the string to convert
    std::string line("Hi there buddy");

    // 2. We want to search for complete words
    std::regex word("(\\w+)");

    // 3. Transform the input string to output lines
    std::transform(
        std::sregex_token_iterator(line.begin(), line.end(), word, 1),
        std::sregex_token_iterator(),
        std::ostream_iterator<std::string>(std::cout, "\n"),
        [](const std::string & w)     // ******* Must be   const *******
            static int i = 1; 
            std::string s = w; 
            s[0] = ::toupper(s[0]);
            return std::string("List[") + std::to_string(i++) + "]=" + s; 
        
    );
    return 0;

【问题讨论】:

不标记为const是不行的,只要你没有实际修改它,但它确实应该是const,因为你没有修改它无论如何。 我想修改它。首字母大写。所以,我介绍了一个临时的。我想避免的。 我的意思是,你不能修改w。复制它然后修改副本不算作修改参数。 【参考方案1】:

我不完全理解这一点。我是否需要使用 const& 参数

关键是你不应该改变输入迭代器指向的值,只能从中读取。这是一个输入迭代器,而不是一个输出迭代器。

正如您链接的 std::transform 文档所说:

unary_op 和 binary_op 不能有副作用。

改变输入将被视为副作用。运算符应该接受旧值并返回新值,仅此而已。

是否需要使用 const& 参数

不可以,但您不能改变原始对象。文档明确说明

签名不需要有 const &。 类型 Type 必须使得 InputIt 类型的对象可以被取消引用,然后隐式转换为 Type。

所以,你可以采取隐式复制

[](std::string s)  /* no need to copy from w explicitly */ 

或使用std::string_view(implicit conversion 来自std::string

[](std::string_view v)  /* ... */ 

【讨论】:

输入迭代器。这是重点。这就是原因的解释。而且我习惯于在 lambdas 中引用参数,以至于我没有采用按值传递的解决方案。逆天。谢谢【参考方案2】:

我是否需要使用const&amp; 参数?

您不需要一个,但拥有一个通常是有意义的。首先,考虑到std::transform 要求unary_op 没有任何 副作用。这意味着您无论如何都不能更改参数。接下来,请注意unary_op 的参数是取消引用的输入迭代器,如您所知。这可以是左值,也可以是右值,具体取决于您传递给std::transform 的输入序列。在您的情况下,取消引用 std::regex_token_iterator 会产生一个 std::sub_match,它具有到底层字符串类型 (this one) 的隐式转换运算符 - 对于您来说,这又是 std::string。现在这是一个右值,一个右值只能绑定到一个const 限定的引用(这就是为什么删除const 并保留该引用不能编译的原因)。你还有另一个理由来输入const

总而言之,您有以下选择:

通过const 参考。这应该是您使用 std::transform 时的默认设置。 按值传递。如果您想更改输入序列的副本以进行进一步处理,请执行此操作。这不会导致副作用,因此是允许的。 如果取消引用迭代器会产生一个临时值,您还可以传递一个右值引用(在您的情况下为std::string&amp;&amp;)。对其进行修改或移动构造是合法的,因为这不会影响std::transform 输入序列,而只会影响临时序列。

在您的情况下,我会按值传递并删除副本 std::string w = v。这将构造 lambda 操作的 std::string 对象(无副本)。

【讨论】:

副作用和价值传递。比恩x。你和用户“没用”给出了很好的解释。两者都有。

以上是关于lambda(参数)中的 const 限定符,用于带有 std::sregex_token_iterator 的 std::transform的主要内容,如果未能解决你的问题,请参考以下文章

自动检测是否需要在函数参数中添加“const”限定符

Const限定符和前向引用

将 'const QVariant' 作为 'this' 参数传递会丢弃限定符 [-fpermissive]

错误:将“const SunscreenSPF”作为“this”参数传递会丢弃限定符问题

C++ 错误 - 将 'const Dataset<double>' 作为 'this' 参数传递会丢弃限定符 [-fpermissive]

C++ Boost - 序列化错误 - 将“const B”作为“this”参数传递会丢弃限定符