

C++11に実装されているstd::regexについて、一部うまく動かない機能があるようです。

std::regex_searchのオーバーロードのうち、以下のコードで使われているものがコンパイルが通りません。

[code]

    #include <iostream>
    #include <regex>
    
    
    
    int main(void) {
        std::string str = std::string("foobar");
        std::regex re(R"(foo)");
        std::smatch match;
    
        std::regex_search(str.begin(), str.end(), match, re);
    
        if(match.size() > 0)
            std::cout << match[0].str();
    
        return 0;
    }
    
[/code]

このコードをコンパイルすると、「std::regex_searchに与えられた引数と一致するオーバーロードが存在しない」という旨のエラーを吐きます。  
そこで原因を調べてみると、どうやら「stringのイテレーターを与え」、なおかつ「match_resultsとしてstd::smatchを与える」ときにこのエラーを吐くようだということがわかってきました。

ここでregex_searchのうち、目的のオーバーロードの定義を見てみますと、

[code]

    template<class _BidIt,
        class _Alloc,
        class _Elem,
        class _RxTraits> inline
        bool regex_match(_BidIt _First, _BidIt _Last,
            match_results<_BidIt, _Alloc>& _Matches,
            const basic_regex<_Elem, _RxTraits>& _Re,
            regex_constants::match_flag_type _Flgs =
                regex_constants::match_default);
    
[/code]

となっています。ここで先ほどの関数呼び出しと照らし合わせてみますと、テンプレートのそれぞれの型は、  
_BidIt = std::string::iterator  
_Elem = char (std::regexの定義が「typedef basic_regex<char> regex;」になっていることより)  
になっています。(_Alloc,_RxTraitsは今回は関係ないので省略(デフォルト指定がなされているので無視できる))

よって、関数の定義を書き直すと以下のようになります。

[code]

    bool regex_match(std::string::iterator _First,
        std::string::iterator _Last,
        match_results<std::string::iterator>& _Matches,
        const basic_regex<char>& _Re);
    
[/code]

さて、これに引数の型がマッチしないということですから、_Matchesの型がマッチしていないということになります。(_Matchesのみテンプレート型の決定に関与していないため)  
ここでstd::smatchの定義を確認すると、

[code]

    typedef match_results<string::const_iterator> smatch;
    
[/code]

となっており、match_resultsに与えられているテンプレート型が「iterator」と「const_iterator」との異なる型になっており、確かに一致していません。(やっと原因判明)  
まあ、確かにこれではコンパイルが通らないのもうなずけます。

そこで、以下のようなコードに書き換えると、確かにうまく動きます。

[code]

    #include <iostream>
    #include <regex>
    
    namespace myStd {
        typedef std::match_results<std::string::iterator> smatch;
    }
    
    int main(void) {
        std::string str = std::string("foobar");
        std::regex re(R"(foo)");
        myStd::smatch match;
    
        std::regex_search(str.begin(), str.end(), match, re);
    
        if(match.size() > 0)
            std::cout << match[0].str();
    
        return 0;
    }
    
[/code]

（逆に、関数std::regex_searchにイテレーターを与える際に(std::string::const_iterator)にキャストしてやってもうまく動きます）

しかし、このようにstdの定義を自分で勝手に書き換えて使うというのは何だか変な感じです。  
インターネット上で調べてもこの問題に関する言及が特にありませんし、原因もよくわかりませんでした。

そこで、この問題に対する最適解はどのようになるのか、結局この問題の根本的な原因はいったい何なのか、を教えていただけるとありがたいです。


CC BY-SA 3.0
2016-07-27T13:33:04.087
27891
2016-07-27T14:33:09.253
17302
question
c++ c++11
std::regex_searchの実装ミス？


[`std::regex_search`](http://en.cppreference.com/w/cpp/regex/regex_search)に用意されている７つのオーバーロードのうちの

[code]

    template< class BidirIt, 
              class Alloc, class CharT, class Traits >
    bool regex_search( BidirIt first, BidirIt last,
                       std::match_results<BidirIt,Alloc>& m,
                       const std::basic_regex<CharT,Traits>& e,
                       std::regex_constants::match_flag_type flags = 
                           std::regex_constants::match_default );
    
[/code]

を期待しているわけですが、第１・２引数は`str.begin()`ということで`BidirIt`テンプレート仮引数は`std::string::iterator`型を指定しようとします。その上で第３引数は`match`ということで`std::smatch&`型となっています。

ここで[`std::match_results`](http://en.cppreference.com/w/cpp/regex/match_results)では

[code]

    typedef std::match_results<std::string::const_iterator> std::smatch;
    
[/code]

と定義されていて`BidirIt`テンプレート仮引数は`std::string::const_iterator`型にならなければなりません。

コンパイルエラーの原因は`string::iterator`
`string::const_iterator`の不一致ですので解消方法としても`cbegin(str)` `cend(str)`を使用する方法や、逆に

[code]

    std::match_results<std::string::iterator> match;
    
[/code]

と定義する方法もあります。

>
> ​イテレーターを使う根本的な目的というのはサーチの開始位置や終了位置をプログラムで自在に制御するためであって、そのためには‌​イテレーターをconst値として使うのはあまり意味がない

C++
conceptsで言うと[`Iterator`](http://en.cppreference.com/w/cpp/concept#Iterator)の`InputIterator`のことを指されているかもしれませんが、`std::string::iterator`は`OutputIterator`でもあります。つまりイテレーターを受け取った側（今回で言えば`regex_search()`）が値を変更することも許してしまっています。

変更を認めないことを明示するためにも呼び出し側が`string::const_iterator`を明示的に選択する場面もあるかと。

* * *

実装の問題を指摘するのであれば動作確認を行った具体的な実装を提示すべきなような…。


CC BY-SA 3.0
2016-07-27T14:33:09.253
27894
2016-07-27T14:33:09.253
4236
27891
answer