6

C++0x 標準作業草案(セクション 6.5.4) では、範囲ベースの for ループで暗黙的に行われる begin() および end() 呼び出しについて次のように述べています。

'begin' と 'end' は、引数依存のルックアップ (3.4.2) でルックアップされます。この名前検索の目的で、名前空間 std は関連付けられた名前空間です。

これを読むと、begin() と end() の呼び出しに設定されたオーバーロード解決には、次のすべてが含まれることを意味します。

  • 範囲ベースの for ループが使用される場所でスコープ内にある begin() および end() のすべてのオーバーロード (特に、グローバル名前空間内のすべてのオーバーロードがスコープ内になります)
  • 名前空間 std の begin() と end() のすべてのオーバーロード
  • 引数に関連付けられた他のネームスペース内の begin() および end() のすべてのオーバーロード

あれは正しいですか?

g++ 4.6 の動作は、この解釈と一致していないようです。このコードの場合:

#include <utility>

template <typename T, typename U>
T begin(const std::pair<T, U>& p); 

template <typename T, typename U>
U end(const std::pair<T, U>& p); 

int main()
{
    std::pair<int*, int*> p;
    for (int x : p)
        ;
}

次のエラーが発生します。

adl1.cpp: In function 'int main()':
adl1.cpp:12:18: error: No match for 'begin(pair<int *, int *> &)'
adl1.cpp:12:18: candidate is:
/usr/local/lib/gcc/i686-pc-linux-
    gnu/4.6.0/../../../../include/c++/4.6.0/initializer_list:86:38: template<
        class _Tp> constexpr const _Tp * begin(initializer_list<_Tp>)
adl1.cpp:12:18: error: No match for 'end(pair<int *, int *> &)'
adl1.cpp:12:18: candidate is:
/usr/local/lib/gcc/i686-pc-linux-
    gnu/4.6.0/../../../../include/c++/4.6.0/initializer_list:96:36: template<
        class _Tp> constexpr const _Tp * end(initializer_list<_Tp>)

これは、グローバル名前空間のオーバーロードではなく、名前空間 std のオーバーロードのみを考慮していることを示唆しています。

ただし、グローバル名前空間で宣言された独自のペア クラスを使用すると、正常にコンパイルされます。

template <typename T, typename U>
struct my_pair
{
    T first;
    U second;
};

template <typename T, typename U>
T begin(const my_pair<T, U>& p); 

template <typename T, typename U>
U end(const my_pair<T, U>& p); 

int main()
{
    my_pair<int*, int*> p;
    for (int x : p)
        ;
}

最終テストとして、my_pair を別の名前空間に配置してみました。

namespace my
{

template <typename T, typename U>
struct my_pair
{
    T first;
    U second;
};

}

template <typename T, typename U>
T begin(const my::my_pair<T, U>& p); 

template <typename T, typename U>
U end(const my::my_pair<T, U>& p); 

int main()
{
    my::my_pair<int*, int*> p;
    for (int x : p)
        ;
}

そして再びエラーが発生します:

adl3.cpp: In function 'int main()':
adl3.cpp:22:18: error: 'begin' was not declared in this scope
adl3.cpp:22:18: suggested alternative:
adl3.cpp:14:35:   'begin'
adl3.cpp:22:18: error: 'end' was not declared in this scope
adl3.cpp:22:18: suggested alternative:
adl3.cpp:17:33:   'end'

そのため、名前空間 std およびその他の関連する名前空間のオーバーロードのみを考慮しているようであり、呼び出しサイトでスコープ内にあるオーバーロードは考慮していないようです (上記のリストの最初の箇条書き)。

これは gcc のバグですか、それとも標準を誤解していますか?

後者の場合、範囲ベースの for ループで std::pair オブジェクトを範囲として扱うことは不可能であることを意味しますか (std::begin() と std::end() をオーバーロードする必要はありません)。間違いは許されません)?

4

1 に答える 1

4

私はこれがgccのバグのように見えることを最初に報告しました。forループの仕様のこの部分でさえ不明確であるように思われ、委員会で調査が開始されました。

範囲ベースのforループルールはまもなく変更されるようです。

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2011/n3257.pdf

また、N3257にリストされているオプションが選択されるかどうかはわかりません。

于 2011-03-04T23:09:46.063 に答える