2017-04-19 9 views
1

私はコンテナを反復処理するための2つのテンプレート関数を考えてみましょう:テンプレート名をメソッドにどのように渡しますか?

template <typename I, typename C> 
It Prev(I i, const C& c) noexcept { 
    Expects(i != c.end()); 
    if (i == c.begin()) return c.end(); 
    return i - 1; 
} 

template <typename I, typename C> 
It Next(I i, const C& c) noexcept { 
    Expects(i != c.end()); 
    return i + 1; 
} 

そして、これらのテンプレートを使用GetNextElementとGetPrevElementを持つクラス:

struct MyClass { 
    std::vector<int> elements; 

    int* GetNextElement(std::vector<int>::iterator i) { 
    auto next = Next(i, elements); 
    if (next == elements.end()) return nullptr; 
    return &*it; 
    } 
    int* GetPrevElement(std::vector<int>::iterator i) { 
    auto prev = Prev(i, elements); 
    if (prev == elements.end()) return nullptr; 
    return &*it; 
    } 
}; 

これらの2つのメソッドは、異なるテンプレートを呼び出す以外は同じことを行います。どのように私はその後

MyClass mc; 
// ... 
auto it = mc.elements.begin(); 
auto next = mc.GetElement<Next>(it); 
// Or maybe mc.GetElement(it, Next); 
+0

'Next'と' Prev'がイテレータに関連付けられたコンテナへの参照を期待しています。 'GetElement'はコンテナを認識しません。あなたは 'std :: vector :: iterator'sを使用しているようですので、これを達成する方法はわかりません。 'GetElement'にコンテナを渡すか、' Next'と 'Prev'からその要件を削除する必要があります。 –

+0

'Expects'が何であるかを定義していません。 'Prev'と' Next'の戻り値の型として 'It'を使いますが、宣言されていません。あなたがタイプ 'I'を使うことになったようです。 –

+0

私の例ではあまりに単純化されているかもしれませんし、呼び出し側が気づかないことを公開しているかもしれません。呼び出し元は、要素がクラスにどのように格納されているのかわかりません。彼は要素に識別子を渡すことができ、 "GetNextElement"または "GetPrevElement"を呼び出すことができることを知っているだけです。これらの2つのメソッドはほぼ同じように、私は1つのメソッドを持つことができ、呼び出し側が希望する要素を取得するために、NextまたはPrevテンプレート関数を使用する必要があるかどうかを指定できます。 – Kian

答えて

1

structに関数をラップすることができれば、この問題は大幅に簡素化されます。

struct Prev 
{ 
    template <typename I, typename C> 
    I operator()(I i, const C& c) noexcept { 
     Expects(i != c.end()); 
     if (i == c.begin()) return c.end(); 
     return i - 1; 
    } 
}; 

struct Next 
{ 
    template <typename I, typename C> 
    I operator()(I i, const C& c) noexcept { 
     Expects(i != c.end()); 
     return i + 1; 
    } 
}; 

テンプレート引数として渡すことができます。

struct MyClass 
{ 
public: 
    auto begin() { return elements.begin(); } 
    auto end() { return elements.end(); } 

    template<typename T, typename I> 
    I GetElement(I iter) 
    { 
     return T()(iter, this->elements); 
    } 

private: 
    std::vector<int> elements; 
}; 

int main() 
{ 
    MyClass mc; 
    auto it = mc.begin(); 
    auto next = mc.GetElement<Next>(it); 
    auto prev = mc.GetElement<Prev>(it); 
} 
+0

呼び出し元はmc.elements自体にアクセスすることはできません(プライベートなので)、フリー関数が役に立たないでしょう。私は例を単純化するためにそれを公開しました。私は、呼び出し側がメソッドにNextまたはPrevを使用するように指示することができるようにしたいが、それをインスタンス化するために使用される型は知らない。 – Kian

+0

あなたの例では、 'GetElement 'は 'mc'や' mc.element'のどのインスタンスを使うのかわかりません。 'GetElement 'を 'MyClass'のメンバーメソッドにしますか? –

+0

ああ、私はそれに気づいた。はい、GetElementは、2つのGetPrevElementメソッドとGetNextElementメソッドを置き換えるメンバーとして意図されていました。私はタイプミスを修正しました。ごめんなさい。したがって、編集は2つのメンバーを置き換えるのではなく、2つのメンバーを呼び出すため、実際に問題に対処しません。 – Kian

2
#define OINVOKE(...) __VA_ARGS__(decltype(args)(args)...) 
#define OVERLOADS_OF(...) [](auto&&...args)\ 
    noexcept(noexcept(OINVOKE(__VA_ARGS__)))\ 
    ->decltype(OINVOKE(__VA_ARGS__))\ 
    { return OINVOKE(__VA_ARGS__); } 

auto fNext=OVERLOADS_OF(Next); 
auto fPrev=OVERLOADS_OF(Prev); 

のように呼ばれるかもしれない単一メンバ関数にこれらを回すん:

template<auto* pf> 
int* GetElement(std::vector<int>::iterator i) { 
    auto next = (*pf)(i, elements); 
    if (next == elements.end()) return nullptr; 
    return &*it; 
} 
auto it = mc.elements.begin(); 
auto next = GetElement<&fNext>(it); 

はC++ 17が必要です。

0

あなたはこのコードを試すことができます。

template <std::vector<int>::iterator (*FOO)(std::vector<int>::iterator, const std::vector<int>&)> 
std::vector<int>::iterator GetElement(std::vector<int>::iterator i) 
{ 
    return FOO(i, elements); 
} 
関連する問題