std::regex_match(和std::regex_search)参数的生命周期限制

3

考虑带有签名 std::regex_match( std::string const&, std::smatch& match, std::regex const& re ) 的 C++11 函数,第一个参数的生命周期有什么限制?我没有发现任何限制,但是当我执行以下程序(使用 VC++ 2010 编译,启用了迭代器调试):

int
main()
{
    std::string a("aaa");
    std::string c("ccc");
    std::regex re("aaa(.*)ccc");
    std::smatch m;
    if (std::regex_match(a + "xyz" + c, m, re)) {
        std::cout << m[0] << std::endl;
        std::cout << m[1] << std::endl;
    }
    return 0;
}

毫无疑问,IT出现了崩溃,原因是m中的sub_match仅保留了字符串的迭代器而非副本。在标准中,我找不到任何禁止我的代码的内容。

顺便说一下:即使在基于std::regexboost::regex中也不能正常工作。(当然,Boost也没有文档说明寿命的限制。)

最后,我想问的是:我应该向标准组织提交DR还是向Microsoft提交错误报告?


我会发送一个DR。现在,DR中应该包含什么是另一个问题。选项可以是仅记录或提供一个接受rvalue引用的重载,以便看起来正确的简单代码能够正确地执行。[我倾向于采用文档方法] - David Rodríguez - dribeas
@DavidRodríguez-dribeas 基于什么?标准库中有很多函数都使用引用,而所需的生命周期从未超过函数本身。除了 Boost 所做的之外,我们基于什么来假设这不是这里的意图呢? - James Kanze
好的,你是说这可能是实现中的一个bug,并且标准可能被设计成复制参数。是的,那是第三种方法 :) - David Rodríguez - dribeas
@DavidRodríguez-dribeas 是的,我的意思是我不知道意图是什么。Pete的回答表明委员会没有考虑这个问题,所以没有明确的意图。这意味着他们下一次讨论时将会决定。 - James Kanze
2个回答

3
我不记得在采用tr1::regexstd::regex时有关此可能性的任何讨论,因此我认为它根本没有被考虑到。事后看来,这肯定是一个我们应该预见到的陷阱。仅凭我的直觉,如果有一个重载函数使用std::string&&,则意味着涉及临时变量并需要复制。因此,我会向标准委员会报告此问题。(完全透明度:我编写了Dinkumware实现,这是Microsoft所提供的)。

而且您可能是基于Boost构建的,它也有相同的问题(包括缺乏与任何要求相关的文档)。这通常不是一个问题,因为您通常不会尝试匹配临时对象。(另一方面,我很容易看到在循环内部匹配一个字符串本地变量,然后在循环结束后使用循环外的smatch)。 - James Kanze
1
@JamesKanze - 这并不是基于 Boost。只是在这方面存在并行的视觉缺陷。 - Pete Becker

1

这个regex_match的重载规范说明它(28.11.2[re.alg.match]/6):

返回: regex_match(s.begin(), s.end(), m, e, flags)

这个重载没有额外的要求,而它委托的重载只需要一个迭代器范围--它甚至不知道有一个字符串需要保持活动状态,因此也没有办法让它保持临时字符串的生命。

这个问题在STL'sregex presentation at C++Now '12的讨论中出现了。有人建议在规范中添加额外的重载,以捕获rvalue字符串参数(例如basic_string<...>&&),这将给出一个很好的编译错误,而不是运行时错误。虽然库规范没有包括这些重载,但我没有看到关于这个问题的缺陷报告。


问题不仅仅局限于 rvalue。考虑在循环外声明 smatch,并在循环内部调用 regex_match,同时在循环内定义一个字符串。这个问题比较复杂:可以通过在匹配时保留字符串的副本来使其正常工作,因此可能应该要求它正常工作。 - James Kanze
1
@JamesKanze - 很好的观点。但是暗示是库通常无法检测到这个生命周期问题。保留字符串的副本非常重,所以我目前的倾向是将其保留为原样,并告诉人们“不要那样做”。但是,当然,我不代表委员会发言。 - Pete Becker
@JamesKanze:我不明白这与其他在编译时无法检测到的生命周期问题有何不同:{ std::vector<int> v; return v.begin(); }是良好形式的代码,而且没有办法(在所有情况下)检测到函数返回后已失效的迭代器。至少,删除的右值重载将有助于检测明显的误用。在我看来,match_results已经被充分指定:它只处理迭代器,这表明应该注意它们所指向的范围的生命周期。 - James McNellis
@PeteBecker 我认为限制生命周期是一个有效的选择(不限制也是一样的);如果委员会决定这样做,我肯定能够接受。但如果有这样的限制,它必须是明确的。(这不像其他 James 的例子,你明确地将迭代器操纵成了一个集合。我认为标准在这里很清楚:在集合停止存在后继续使用迭代器是未定义行为。) - James Kanze

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接