模板化函数,适用于原始指针上的迭代器以及unique_ptrs上的迭代器

Tho*_* B. 6 c++ templates iterator unique-ptr c++11

假设我有一个模板函数,它接受某种指针集合的const范围(或更好的开始和结束迭代器).此函数在内部构造一个STL容器,其中包含指针以重新组织元素.

现在我想为unique_ptr-collections重用这个函数.我不知何故需要修改模板参数或引入新的包装器或重载......但是如何?有没有C++ 11模板魔术,STL助手或助推助手?下面是一个示例代码:

#include <string>
#include <iostream>
#include <vector>
#include <algorithm>
#include <memory>

// Element Class
class Foo { };

// Take a range of elements, sort them internally by their addresses and print them in order    
template <typename FooIterator>
void print_sorted_addresses(FooIterator beginFoos, FooIterator endFoos)
{
    // Sort them
    std::vector<const Foo*> elements(beginFoos, endFoos);
    std::sort(elements.begin(), elements.end());
    // Print them
    for(const auto& e : elements)
        std::cout << e << std::endl;
}

int main() {
    std::vector<Foo*> raw_foos;
    std::vector<std::unique_ptr<Foo>> unique_foos;

    // Fill them
    for(int i=0; i<10; i++) {
        std::unique_ptr<Foo> foo(new Foo());
        raw_foos.push_back(foo.get());
        unique_foos.push_back(std::move(foo));
    }

    print_sorted_addresses(raw_foos.cbegin(), raw_foos.cend());
    //print_sorted_Foos(unique_foos.cbegin(), unique_foos.cend()); // ERROR

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

罪魁祸首似乎是原始指针和智能指针(unique_ptr特别是)将它们转换为原始指针的非均匀行为.这可以通过解引用循环àla来规避std::addressof(*p),但是如果p不是,则只有明确定义的行为nullptr.为了减少我使用条件模板进行的任何运行时检查,并提出以下内容:

template<typename Ptr> using RawPtr = typename std::pointer_traits<Ptr>::element_type*; 

// raw pointers like int**, const char*, ...
template<typename Ptr>
typename std::enable_if<std::is_pointer<Ptr>::value, RawPtr<Ptr>>::type make_raw(Ptr ptr) { return ptr; }

// smart pointers like unique_ptr, shared_ptr, ... 
template<typename Ptr>
typename std::enable_if<!std::is_pointer<Ptr>::value, RawPtr<Ptr>>::type make_raw(Ptr& ptr) { return ptr.get(); }
Run Code Online (Sandbox Code Playgroud)

这可以在@tclamb的迭代器中使用,或者在@ Praetorian的答案中在boost :: transform_iterator中使用.但是构建特定的get() - 智能指针实现的成员而不是操作符*-interface使得指针成为指针仍然感觉很奇怪.

tcl*_*amb 3

这是包装指针迭代器的通用方法。在取消引用时,它取消引用存储的迭代器(产生(智能)指针),并再次取消引用(产生对被指向者的引用),然后返回被指向者的地址(通过std::addressof())。其余的实现只是迭代器样板。

template<typename Iterator,
         typename Address = decltype(std::addressof(**std::declval<Iterator>()))
         >
class address_iterator : public std::iterator<std::input_iterator_tag, Address>
{
public:
    address_iterator(Iterator i) : i_{std::move(i)} {};

    Address operator*() const {
        auto&& ptr = *i_;
        return i_ == nullptr ? nullptr : std::addressof(*ptr);
    };

    Address operator->() const {
        return operator*();
    }

    address_iterator& operator++() {
        ++i_;
        return *this;
    };

    address_iterator operator++(int) {
        auto old = *this;
        operator++();
        return old;
    }

    bool operator==(address_iterator const& other) const {
        return i_ == other.i_;
    }

private:
    Iterator i_;
};

template<typename I, typename A>
bool operator!=(address_iterator<I, A> const& lhs, address_iterator<I, A> const& rhs) {
    return !(lhs == rhs);
}

template<typename Iterator>
address_iterator<Iterator> make_address_iterator(Iterator i) {
    return i;
}
Run Code Online (Sandbox Code Playgroud)

Coliru上的实例(std::random_shuffle()为了好玩而投入)。:)