2016-05-12 10 views
5

私は、私は1つのループの両方を統一したかった二つの流れ、コピーできないタイプの範囲ループの場合は可能ですか?

{ 
std::ifstream ifs("A.dat"); 
... code ... 
} 
{ 
std::ifstream ifs("B.dat"); 
... same code ... 
} 

から読み込まれているいくつかの重複コードを持っています。 最初の反応は、これを行うことです。

タイプはコピーできませんので、それがコンパイルされませんので、私は、これは試してみましたが
for(auto ifs : {ifstream("A.dat"), ifstream("B.dat")}) 
{ 
... code ... 
} 

for(auto& ifs : {ifstream("A.dat"), ifstream("B.dat")}) 
{ 
... code ... 
} 

内部ifsので、動作しないことループはconstです。私はこれをやってしまったコースの最後に

for(auto&& ifs : {ifstream("A.dat"), ifstream("B.dat")}) 

:これはどちらか動作しませんでした(。const ifstreamを使用することはできません) 、私は同じ理由だと思います。

#include<iostream> 
int main(){ 
for(auto& name : {"A.dat", "B.dat"}) 
{ 
    std::ifstream ifs(name); 
    ... code ... 
} 

しかし、std::ifstreamのようなタイプと直接forループの範囲を持つことが可能であるならば、私はまだ好奇心?

for (auto& ifs: std::make_array(std::ifstream("A.dat"), std::ifstream("B.dat"))) 
{ 
    // ... 
} 

をし、さらにハックで:

for (auto& ifs: std::array<std::ifstream, 2>{std::ifstream("A.dat"), std::ifstream("B.dat")}) 
{ 
    // ... 
} 

TS2からstd::make_arrayhttp://en.cppreference.com/w/cpp/experimental/make_array)で、これはあまりにも動作します:@ SamVarshavchikの答えに触発さ

+0

は、ストリームイテレータを見上げて疑問に思ったことは、醜いhaxなしでずっと助けてくれるでしょう –

+0

@LightnessRacesinOrbit、hacks、はい、私はfor-loop変数を 'const_cast'することができました。 – alfC

+0

'initializer_list'はその要素への' const'アクセスしか許可しないので、あなたはどこかの醜さに頼らずにあなたが望むことをすることができません。 – Praetorian

答えて

5
std::ifstream streams[2]; 

streams[0].open("A.dat"); 
streams[1].open("B.dat"); 

for (auto &stream:streams) 
{ 
    // ... 
} 
+0

'std :: ifstream streams [] {std :: ifstream(" A.dat ")、std :: ifstream(" B.dat ")} ; for(auto&stream:streams){} ' – Praetorian

+0

これは、' std :: array'を使ってもうまくいくと思いました。私の答えを見てください。 – alfC

0

私はこれが機能することを発見しました

template < class TT, class... Types> 
constexpr std::array<TT, sizeof...(Types)> make_array_of(Types&&... t) { 
    return {TT(std::forward<Types>(t))... }; 
} 

私はC++で

for (auto& ifs: make_array_of<std::ifstream>("A.dat", "B.dat")){ 
... 
} 
1

を行うことができ、すべてが可能です。まあ

int main() 
{ 
    std::string buffer; 
    for (auto& stream : streams(std::istringstream("hello world"), 
           std::istringstream("funky chicken"), 
           std::ifstream("foo.txt"))) 
    { 
     while (stream >> buffer) 
     { 
      std::cout << buffer << std::endl; 
     } 
    } 
} 

を今することができます:

はこのように、ストリームの任意の種類のコレクションをイテレータできることを想像してみてください。見てください:istreamの多形の一時的なコンテナとイテレータ。

同様の手法は、共通の多相インタフェースを共有するオブジェクトの任意のコレクションに対して機能します。

期待出力

#include <iostream> 
#include <fstream> 
#include <sstream> 
#include <utility> 
#include <tuple> 
#include <array> 


namespace detail { 

    template<class Interface> 
    struct iface_iter 
    { 
     using value_type = Interface; 
     using reference = Interface&; 

     using internal_p = value_type * const *; 

     iface_iter(internal_p pp) : _pp(pp) {} 

     reference operator*() const { 
      return **_pp; 
     } 

     iface_iter& operator++() { 
      ++_pp; 
      return *this; 
     } 

     bool operator==(const iface_iter& r) const { 
      return _pp == r._pp; 
     } 

     bool operator!=(const iface_iter& r) const { 
      return !(*this == r); 
     } 

     internal_p _pp; 
    }; 

    template<class CommonType, class...Streams> 
    struct common_sequence 
    { 
     using common_type = CommonType; 
     using iterator = iface_iter<common_type>; 

     constexpr static std::size_t size() { return sizeof...(Streams); } 

     using storage_type = std::tuple<Streams...>; 
     using pointer_array = std::array<common_type*, size()>; 

     common_sequence(Streams...streams) 
     : _storage(std::move(streams)...) 
     , _pointers(build_pointers(std::make_index_sequence<size()>(), _storage)) 
     {} 

     common_sequence(common_sequence&& r) 
     : _storage(std::move(r._storage)) 
     , _pointers(build_pointers(std::make_index_sequence<size()>(), _storage)) 
     { 
     } 

     common_sequence& operator=(common_sequence&& r) 
     { 
      _storage = std::move(r._storage); 
      _pointers = build_pointers(std::make_index_sequence<size()>(), _storage); 
     } 

     template<std::size_t I> 
     using stream_type = std::tuple_element_t<I, storage_type>; 


     template<std::size_t...Is> 
     static constexpr 
     pointer_array build_pointers(std::index_sequence<Is...>, 
            std::tuple<Streams...>& tup) 
     { 
      return pointer_array { 
       static_cast<common_type*>(&static_cast<stream_type<Is>&>(std::get<Is>(tup)))... 
      }; 
     } 

     iterator begin() const { 
      return { _pointers.data() }; 
     } 

     iterator end() const { 
      return { _pointers.data() + size() }; 
     } 

     mutable storage_type _storage; 
     pointer_array _pointers; 

    }; 

} 

template<class CommonBase, class...Things> 
auto make_common_sequence(Things&&...ts) 
{ 
    return detail::common_sequence<CommonBase, std::decay_t<Things>...>(std::move(ts)...); 
} 

template<class...Streams> 
auto streams(Streams&&...strs) 
{ 
    return make_common_sequence<std::istream>(std::move(strs)...); 
} 

struct base 
{ 
    virtual void foo() = 0; 
}; 

struct d1 : base 
{ 
    void foo() override { std::cout << "d1::foo" << std::endl; } 
}; 

struct d2 : base 
{ 
    void foo() override { std::cout << "d2::foo" << std::endl; } 
}; 

template<class...Ts> 
auto bases(Ts&&...ts) 
{ 
    return make_common_sequence<base>(std::move(ts)...); 
} 


int main() 
{ 
    std::string buffer; 
    for (auto& stream : streams(std::istringstream("hello world"), 
           std::istringstream("funky chicken"), 
           std::ifstream("foo.txt"))) 
    { 
     while (stream >> buffer) 
     { 
      std::cout << buffer << std::endl; 
     } 
    } 

    for (auto& f : bases(d1(), d2(), d1(), d2())) 
    { 
     f.foo(); 
    } 

    return 0; 
} 
:もちろん

hello 
world 
funky 
chicken 
... plus whatever is in foo.txt ... 
d1::foo 
d2::foo 
d1::foo 
d2::foo 

、我々は多型を必要としない場合は、単純なテンプレート可変長引数のイテレータは十分でしょう。私ものの

template<class F, class...Things> 
void apply_to_all(F f, Things... things) 
{ 
    using expand = int[]; 
    void(expand{ 0, 
     (f(things), 0)... 
    }); 
} 

int main() 
{ 
    std::string buffer; 
    apply_to_all([&](auto& stream) 
       { 
        while (stream >> buffer) 
        { 
         std::cout << buffer << std::endl; 
        } 
       }, 
       std::istringstream("hello world"), 
       std::istringstream("funky chicken"), 
       std::ifstream("foo.txt")); 
} 
+0

これは私が尋ねたもの(多型性)を超えていますが、非常に印象的です。 – alfC

+0

これはポリモーフィックコンテナ問題の非常に一般的な解決法です。最も一般的なインターフェース(この場合は 'istream'クラス)を自動的に検出できるかどうか知っていますか? – alfC

+0

@alfC私の瞬間を教えてください... –

関連する問題