11

私はこのようなコードを持っています

std::ifstream file(filename, std::ios_base::in);
if(file.good())
{
    file.imbue(std::locale(std::locale(), new delimeter_tokens()));
    for(auto& entry : std::istream_iterator<std::string>(file))
    {
        std::cout << entry << std::endl;    
    }
}
file.close();

ここで、とは次std::istream_iterator<std::string>の ように定義されますbegin()end()

template<class T>
std::istream_iterator<T> begin(std::istream_iterator<T>& stream)
{
    return stream;
}

template<class T>
std::istream_iterator<T> end(std::istream_iterator<T>& stream)
{
    return std::istream_iterator<T>();
}

これは、マーク・ネルソンがドブ博士のここにも書いていることです。残念ながら、コードはVisualStudio2012でコンパイルできずエラーメッセージが表示されます

エラーC3312:タイプ'std ::istream_iterator<_Ty>'に対して呼び出し可能な'begin'関数が見つかりません

エラーC3312:タイプ'std ::istream_iterator<_Ty>'に対して呼び出し可能な'end'関数が見つかりません

質問:私が気付いていないこと、コンパイラのバグ(ありそうもないが、念のため)、または...ええと、何かアイデアはありますか?


Xeo のアドバイスに従って、この質問はかなりクリーンアップされます。より多くの背景と参照を提供するために、これはStackoverflowに関する他の質問に関連しています。私は、ラインベースの解析を通常のループよりもクリーンにする方法を考えていました。インターネットからのコーディングとチェックのビット、そして私は次のように作業スケッチを持っていました

std::ifstream file(filename, std::ios_base::in);
if(file.good())
{               
    file.imbue(std::locale(std::locale(), new delimeter_tokens()));
    for(auto& entry : istream_range<std::string>(file)
    {
        std::cout << entry << std::endl;    
    }
}
file.close();

しかし、私が改善しようとしたわずかな障害がありました。コンパイルに失敗し、好きではないコードのように書く方が自然に見えると思います

for(auto& entry : istream_range<std::string>(file)

別のイテレータに注意してください。delimeter_tokensは、Nawazが親切にここに示したように(コードは複製されていません)、istream_rangeはコード合成ブログのように定義されています。前述のCodeSynthesisブログ投稿で宣伝されているように、開始と終了の実装は機能するはずです。

最後のルール(独立したbegin()およびend()関数へのフォールバック)により、既存のコンテナーを範囲ベースのforループインターフェイスに非侵襲的に適合させることができます。

したがって、すべての(無関係な)背景を持つ私の質問。

4

2 に答える 2

8

Ranged-forは、ネイティブ配列(T foo[N])とメンバーbegin/の特別な処理でend結果が得られない場合、ADLに依存します。

§6.5.4 [stmt.ranged] p1

  • それ以外の場合、begin-exprend-exprはそれぞれとです。begin(__range)ここ、とは引数依存のルックアップ(3.4.2)でルックアップされます。この名前空間の検索では、名前空間は関連付けられた名前空間です。end(__range)beginendstd

問題は、の関連付けられた名前空間std::istream_iteratorが(明らかに)namespace stdグローバル名前空間ではないということです。

§3.4.2 [basic.lookup.argdep] p2

関数呼び出しの引数タイプごとTに、0個以上の関連付けられた名前空間のセットと、考慮される0個以上の関連付けられたクラスのセットがあります。名前名とクラスのセットは、関数の引数のタイプによって完全に決定されます[...]。

  • が基本タイプの場合T、関連する名前名とクラスのセットは両方とも空です。
  • Tがクラスタイプ(ユニオンを含む)の場合、関連するクラスは次のとおりです。クラス自体メンバーであるクラス(ある場合)。およびその直接および間接の基本クラス。関連する名前名は、関連するクラスがメンバーである名前名です。さらに、Tがクラステンプレートの特殊化である場合、それに関連付けられた名前空間とクラスには、次のものも含まれます。テンプレート型パラメーターに提供されたテンプレート引数の型に関連付けられた名前空間とクラス[...]。

2番目の箇条書きの最後の(引用された)部分に注意してください。これは基本的に、グローバル名前空間のメンバーであるクラスをテンプレート引数として使用すると、コードが機能することを意味します。

#include <iterator>
#include <iostream>

template<class T>
std::istream_iterator<T> begin(std::istream_iterator<T> is){
  return is;
}
template<class T>
std::istream_iterator<T> end(std::istream_iterator<T>){
  return std::istream_iterator<T>();
}

struct foo{};

std::istream& operator>>(std::istream& is, foo){
  return is;
}

int main(){
  for(foo f : std::istream_iterator<foo>(std::cin))
  //                                ^^^
  // make global namespace one of the associated namespaces
    ;
}
于 2012-07-01T14:14:00.913 に答える
1

引数に依存するルックアップのため、コンパイラは名前空間で検索begin()を試みます。関数をそこに置くと、コードがコンパイルされます。end()std

名前の検索はC++の複雑な問題であるため、コンパイラが正しく動作しているかどうかは完全にはわかりません。

于 2012-07-01T13:56:12.507 に答える