本文发自 http://www.binss.me/blog/the-problem-of-gcc4.8-when-using-the-c++11-regex-functionality/,转载请注明出处。

在使用C++11的新函数regex_match时,发现了一个奇怪的问题:

std::string s ("Accept: text/html");
std::regex e ("(.*): (.*)");
smatch sm;
regex_match(s, sm, e);

此段函数用于提取HTTP header中的name和value,在MAC下测试通过,sm在regex_match调用后size为3,符合预期:

  • sm[0]为 Accept: text/html
  • sm[1]为 Accept
  • sm[2]为 text/html

结果在集成到HTTPServer后,执行结果与预期不匹配:

  • sm[0]为 Accept: text/html
  • sm[1]为 Accept: text/html
  • sm[2]为 text/html

难道是我的正则表达式有误?掏出了多年以前的编译原理,研究了半天,好像并非如此。于是我尝试官方的例子:

int main()
{
    std::string s ("subject");
    std::regex e ("(sub)(.*)");
    smatch sm;
    regex_match(s, sm, e);
    for (unsigned i=0; i<sm.size(); ++i)
    {
        std::cout << "[" << sm[i] << "] ";
    }
    cout<<endl;
}

官方文档中,输出为[subject] [sub] [ject],而在测试中,输出为[subject] [sub] [bject],这说明问题不是出在正则表达式上。于是我把目光投向了编译器:

$ g++ -v
Apple LLVM version 7.0.0 (clang-700.0.72)

而HTTPServer是放在ubuntu14.04的docker容器中编译的:

# g++ -v
gcc version 4.8.4 (Ubuntu 4.8.4-2ubuntu1~14.04)

果然不同,登录到gcc版本同是4.8.4的VPS上编译,得到了和HTTPServer相同的结果。

难道真的是编译器的锅,Google之:g++ 4.8 regex

结果发现了以下页面:

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=53631 http://stackoverflow.com/questions/23474121/what-part-of-regex-is-supported-by-gcc-4-9

Following information can be found from GCC 4.9 release notes:

"Support for various C++14 additions have been added to the C++ Front End, on the standard C++ library side the most important addition is support for the C++11 regex"

也就是说在4.9版本以前的实现regex不是我们在C++11文档中查到的那些,换言之,锅就是GCC的,解决的办法就是把GCC升级到4.9:

# apt-get install g++-4.9
Reading package lists... Done
Building dependency tree
Reading state information... Done
E: Unable to locate package g++-4.9
E: Couldn't find any package by regex 'g++-4.9'

果然找不到,因为好像昨天才执行过apt-get upgrade,继续Google之,网上说可以通过PPA来安装:

# apt-get install software-properties-common
# add-apt-repository ppa:ubuntu-toolchain-r/test
# apt-get update
# apt-get install gcc-4.9 g++-4.9

安装完成后,使用g++-4.9命令来编译:

# g++-4.9 -o test test.cpp -std=c++11
# ./test
[subject] [sub] [ject]

终于输出了预期的结果!

由于ppa的原因,我们的系统上如今存在两个版本的GCC,为了方便今后使用,将默认版本设置为4.9版本:

# update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-4.9 60 --slave /usr/bin/g++ g++ /usr/bin/g++-4.9

今后可以使用命令在两个版本间进行切换:

# update-alternatives --config gcc
There are 2 choices for the alternative gcc (providing /usr/bin/gcc).

  Selection    Path              Priority   Status
------------------------------------------------------------
  0            /usr/bin/gcc-4.8   60        auto mode
  1            /usr/bin/gcc-4.8   60        manual mode
* 2            /usr/bin/gcc-4.9   60        manual mode

输入数字即可切换到相应版本。