簡體   English   中英

根據參數類返回模板迭代器

[英]Return template iterator based on argument class

我正在寫一個使用迭代器的算法函數。 此函數應同時與普通和常量迭代器一起使用,重要的是,這些迭代器來自的類不是模板,我事先知道它。

在下面的定義中,有什么方法可以強制迭代器來自特定類?

// This is an example, A could be any other class with exposed iterators.
using A = std::vector<int>;

// How to enforce that Iterator is an iterator from A?
template <typename Iterator>
Iterator foo(Iterator begin, Iterator end);

...

A a;
auto it = foo(a.begin(), a.end());
*it = 4; // Must compile

// --------

const A a;
auto it = foo(a.begin(), a.end());
*it = 4; // Must not compile

// --------

B b;
auto it = foo(b.begin(), b.end()); // Should not compile.

在這種情況下, foo不會直接修改提供的范圍,但是如果提供的范圍首先是可修改的,則允許修改結果迭代器。 如果可以在不復制代碼的情況下完成此操作,那就太好了。

只是不使用模板:

A::iterator foo(A::iterator begin, A::iterator end);

您可以使用std :: enable_if:

#include <type_traits>
#include <vector>

class X : public std::vector<int> {};
class Y : public std::vector<double> {};

template <typename Iterator>
typename std::enable_if<std::is_same<Iterator, X::iterator>()
    || std::is_same<Iterator, X::const_iterator>(),
    Iterator>::type
foo(Iterator begin, Iterator end) {
    return begin;
}

int main() {
    X x0;
    auto i0 = foo(x0.begin(), x0.end());
    *i0 = 4; // Must compile

    const X x1;
    auto i1 = foo(x1.begin(), x1.end());
    // error: assignment of read-only location
    //*i1 = 4; // Must not compile

    Y y;
    // error: no type named ‘type’ in ‘struct std::enable_if ...
    //auto i2 = foo(y.begin(), y.end()); // Should not compile
}

或者使用static_assert作為更好的選擇:

template <typename Iterator>
Iterator foo(Iterator begin, Iterator end) {
    static_assert(std::is_same<Iterator, X::iterator>()
        || std::is_same<Iterator, X::const_iterator>(),
        "No X::iteator or X::const_iterator");
    return begin;
}

您可以使用函數重載檢查:

inline void check_must_be_iterator_from_A(A::iterator) {}
inline void check_must_be_iterator_from_A(A::const_iterator) {}

template <typename I>
I foo(I a, I b) {
   typedef void (*must_be_iterator_from_A)(I);
   must_be_iterator_from_A c = &check_must_be_iterator_from_A;
   //...
}

另一個選擇是使用模板特化來創建約束,這使代碼在函數內部更簡單,並且無論編譯器如何都不會對運行時造成不利影響:

template <typename I> struct is_iterator_from_A;
template <> struct is_iterator_from_A<A::iterator>{ enum {ok}; };
template <> struct is_iterator_from_A<A::const_iterator>{ enum {ok}; };

template <typename I>
I bar(I a, I b) {
    is_iterator_from_A<I>::ok;
    return a;
}

我將首先為const迭代器編寫函數,然后為non_const情況在其周圍編寫包裝器:

A::const_iterator foo(A::const_iterator start, A::const_iterator end)  {
    std::cout << " Called foo with const iterators " << std::endl;
    return start;
}

A::iterator foo(A::iterator start, A::iterator end) {
    std::cout << " Called foo with non_const iterators " << std::endl;  
    auto it = foo(static_cast<A::const_iterator>(start), static_cast<A::const_iterator>(end));
    return start + std::distance(static_cast<A::const_iterator>(start), it);
}

如果內聯包裝函數,則應該獲得零(或幾乎零)的開銷。

編輯:如果您的容器不提供隨機訪問迭代器,則distance具有線性復雜度,您將必須使用std::advance而不是“ +”運算符,因此根據您的性能要求,這可能不是可行的解決方案您。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM