C++ 扩展成员选择运算符“->;的实现是否正确std::vector上迭代器的数量? 问题

C++ 扩展成员选择运算符“->;的实现是否正确std::vector上迭代器的数量? 问题,c++,pointers,iterator,operators,stdvector,C++,Pointers,Iterator,Operators,Stdvector,如果value\u type是指针类型,则迭代器的成员选择运算符->无效,因为访问成员时需要间接寻址:(*\u It)->member。如果有代码执行类似于\u It->method()的操作,那么将包含的项的类型更改为指针类型会带来麻烦 这是因为成员选择操作符->的原始实现是: // return pointer to class object pointer operator->() const { return (&**this); } 提议的解决办法 如果value\u

如果
value\u type
是指针类型,则迭代器的成员选择运算符
->
无效,因为访问成员时需要间接寻址:
(*\u It)->member
。如果有代码执行类似于
\u It->method()
的操作,那么将包含的项的类型更改为指针类型会带来麻烦

这是因为成员选择操作符
->
的原始实现是:

// return pointer to class object
pointer operator->() const { return (&**this); }
提议的解决办法 如果
value\u type
是指针类型,则成员选择操作符应返回
reference
,而不是
pointer

详细实施:
我刚刚在
中插入了以下扩展名

现在,以下操作成为可能,保留了成员选择操作符的原始行为:

#include <stdio.h>
#include <tchar.h>
#include <iostream>
#include <vector>

typedef struct udf {
    static int auto_m; int _m;
    udf(): _m(++auto_m) {}
} * udf_ptr;
int udf::auto_m = 0;

int _tmain(int argc, _TCHAR* argv[]) {
    using namespace std;
    typedef vector<udf_ptr> udf_p_v;
    typedef vector<udf> udf_v;
    udf_v s(1); udf_p_v p(1);
    p[0] = &s[0];
    udf_v::iterator s_i(s.begin()); udf_p_v::iterator p_i(p.begin());

    // Now, the indirection on p_i is not needed
    // in order to access the member data _m:
    cout
        << "s_i: " << s_i->_m << endl
        << "p_i: " << p_i->_m << endl;

    return 0;
}
两个问题 是的,如果代码应该由其他人编译,那么替换STL模板的原始实现是一件坏事。如果版权所有者或许可证允许,可以通过提供
的扩展版本来解决此问题。否则,将无法分发此类软件

  • 那么,以所述方式在指针上扩展成员选择运算符的原始实现是否正确?对于内部软件,它可能是正确的。如果源代码应该是开放的,或者由无法获得这些扩展版本的人使用,则情况并非如此。这些情况是什么?还有其他情况吗

  • 是否存在提议的扩展实施无法运行的情况


  • 您不需要为此编写任何代码——Boost已经有了它!请看这里:

    他们的例子是:

    typedef boost::ptr_vector<animal>  ptr_vec;
    ptr_vec vec;
    ptr_vec::iterator i = vec.begin();
    i->eat(); // no indirection needed
    
    typedef boost::ptr_vector ptr_vec;
    ptr_-vec-vec;
    ptr_vec::迭代器i=vec.begin();
    i->eat();//不需要间接干预
    
  • 我认为这不是个好主意。标准库之所以成为标准库是有原因的,人们期望它以标准的方式运行。打破这种期望可能是危险的。我想说,制作自己的迭代器或容器类,或者使用其他库提供的迭代器或容器类,是一个非常好的解决方案

  • 我想这样做可能会破坏一些标准算法

  • 解决这个问题的另一个方法是。下面是发布的代码的样子:

    std::vector<animal*> vec;
    vec.push_back(new animal{});
    auto i = boost::make_indirect_iterator(std::begin(vec));
    i->eat(); // no indirection needed
    
    std::向量向量向量机;
    向量推回(新动物{});
    自动i=boost::make_间接迭代器(std::begin(vec));
    i->eat();//不需要间接干预
    

    这甚至适用于 STD::vector ,如果您想迭代成员容器并隐藏您存储的代码“>代码> STD::UNIQUYGPTR <代码> S.< /P> > P>,C++标准定义了<代码> STD::vector < /COD>及其迭代器的语义,这将非常方便。它指定

    std::vector
    的迭代器中涉及的各种类型的类型。更改实现将破坏标准遵从性

    无论如何还有另一个转折点:虽然
    T*
    是明显的指针类型,但
    std::vector
    实际上包含一系列智能指针(例如
    std::unique_ptr
    )并不少见。类似地,适用于
    std::vector
    的内容也适用于
    std::deque
    std::list

    尽管可以将逻辑折叠到各种容器的实现中,但实际上使用合适的迭代器适配器和合适的函数来获得这些容器更简单、更灵活(因为可能需要其他定制)

    实现指针通用性的第一步是检测智能指针。这很容易通过一个简单的特征实现:

    template <typename T>
    struct is_pointer_like_test {
        template <typename S, typename = decltype(std::declval<S>().operator->())>
        static std::true_type  test(S*);
        template <typename S>
        static std::true_type  test(S**);
        static std::false_type test(void*);
    };
    
    template <typename T>
    struct is_pointer_like
        : decltype(is_pointer_like_test<T>::test(static_cast<T*>(0))) {
    };
    
    其思想是获取指针的指针对象类型,但保留类型本身,因为它不是指针。类似地,指针被解除引用以访问值,而非指针则不会。这是相对较快的,我想它可以改进

    下一步是将这些构建块放在一起,创建指针适配器和一些工厂函数:

    template <typename Iterator>
    class ptr_iterator {
        Iterator it;
        using original_value = typename std::iterator_traits<Iterator>::value_type;
    
    public:
        using iterator_category = typename std::iterator_traits<Iterator>::iterator_category;
        using difference_type = typename std::iterator_traits<Iterator>::difference_type;
        using value_type = typename pointer_helper<original_value>::type;
        using reference  = typename std::add_lvalue_reference<value_type>::type;
        using pointer    = typename std::add_pointer<value_type>::type;
    
        explicit ptr_iterator(Iterator it): it(it) {}
        bool operator==(ptr_iterator const& other) const { return this->it == other.it; }
        bool operator!=(ptr_iterator const& other) const { return !(*this == other); }
        ptr_iterator& operator++() { ++this->it; return *this; }
        ptr_iterator  operator++(int) { ptr_iterator rc(*this); ++this->it; return rc; }
    
        reference operator*() { return pointer_helper<original_value>::value(*it); }
        pointer   operator->() { return &**this; }
        // other iterator operations
    };
    
    template <typename Cont>
    auto ptr_begin(Cont&& cont) -> ptr_iterator<decltype(cont.begin())> {
        return ptr_iterator<decltype(cont.begin())>(cont.begin());
    }
    
    template <typename Cont>
    auto ptr_end(Cont&& cont) -> ptr_iterator<decltype(cont.end())> {
        return ptr_iterator<decltype(cont.begin())>(cont.end());
    }
    

    除了用例二的工作之外,代码没有得到很好的测试。

    这里有一些很好的答案,但我觉得您的问题和建议的解决方案中有些东西没有得到解决

    请引用标准

    当然可以(我的)

    17.6.4.2.1名称空间标准[名称空间标准]

    C++程序的行为是<强>未定义的,如果它将命名空间或定义添加到命名空间STD 或命名空间STD内的命名空间,除非另有说明。只有当声明依赖于用户定义的类型且专门化满足原始模板的标准库要求且未明确禁止时,程序才可以将任何标准库模板的模板专门化添加到命名空间std

    2,C++程序的行为如果声明为

    则是未定义的。 (2.1)-标准库类模板的任何成员函数的显式专门化,或

    (2.2)-标准库类或类模板的任何成员函数模板的显式专门化, 或

    (2.3)-标准库类或类模板的任何成员类模板的显式或部分专门化

    还有更多,链接是(第459页)

    我认为第2.2条和第2.3条很好地涵盖了这一点

    这些都不是针对个人的,而是要以幽默的方式阅读。然而,这个信息是非常严肃的

    如果任何年轻的开发者都有同样的想法

    我刚刚在
    中插入了以下扩展名

    恭喜你,你刚打破了C++发明了一种看起来很像C++的新语言,但是有一些微妙的、未经证实的差异。
    std::vector<animal*> vec;
    vec.push_back(new animal{});
    auto i = boost::make_indirect_iterator(std::begin(vec));
    i->eat(); // no indirection needed
    
    template <typename T>
    struct is_pointer_like_test {
        template <typename S, typename = decltype(std::declval<S>().operator->())>
        static std::true_type  test(S*);
        template <typename S>
        static std::true_type  test(S**);
        static std::false_type test(void*);
    };
    
    template <typename T>
    struct is_pointer_like
        : decltype(is_pointer_like_test<T>::test(static_cast<T*>(0))) {
    };
    
    template <typename T, bool = is_pointer_like<T>::value>
    struct pointer_helper {
        using type = T;
        template <typename S>
        static S value(S&& arg) { return std::forward<S>(arg); }
    };
    template <typename T>
    struct pointer_helper<T, true> {
        using type = typename std::decay<decltype(*std::declval<T>())>::type;
        template <typename S>
        static auto value(S&& arg) -> decltype(*arg) { return *arg; }
    };
    
    template <typename Iterator>
    class ptr_iterator {
        Iterator it;
        using original_value = typename std::iterator_traits<Iterator>::value_type;
    
    public:
        using iterator_category = typename std::iterator_traits<Iterator>::iterator_category;
        using difference_type = typename std::iterator_traits<Iterator>::difference_type;
        using value_type = typename pointer_helper<original_value>::type;
        using reference  = typename std::add_lvalue_reference<value_type>::type;
        using pointer    = typename std::add_pointer<value_type>::type;
    
        explicit ptr_iterator(Iterator it): it(it) {}
        bool operator==(ptr_iterator const& other) const { return this->it == other.it; }
        bool operator!=(ptr_iterator const& other) const { return !(*this == other); }
        ptr_iterator& operator++() { ++this->it; return *this; }
        ptr_iterator  operator++(int) { ptr_iterator rc(*this); ++this->it; return rc; }
    
        reference operator*() { return pointer_helper<original_value>::value(*it); }
        pointer   operator->() { return &**this; }
        // other iterator operations
    };
    
    template <typename Cont>
    auto ptr_begin(Cont&& cont) -> ptr_iterator<decltype(cont.begin())> {
        return ptr_iterator<decltype(cont.begin())>(cont.begin());
    }
    
    template <typename Cont>
    auto ptr_end(Cont&& cont) -> ptr_iterator<decltype(cont.end())> {
        return ptr_iterator<decltype(cont.begin())>(cont.end());
    }
    
    struct foo {
        int value;
        foo(int value): value(value) {}
        void bar() { std::cout << "foo(" << value << ")\n"; }
    };
    
    template <typename Cont>
    void print(Cont&& cont) {
        for (auto it = ptr_begin(cont), end = ptr_end(cont); it != end; ++it) {
            it->bar();
        }
    }
    
    int main()
    {
        std::vector<foo> v;
        v.push_back(1);
        v.push_back(2);
        v.push_back(3);
        print(v);
    
        std::vector<foo*> p;
        p.push_back(&v[2]);
        p.push_back(&v[1]);
        p.push_back(&v[0]);
        print(p);
    
        std::vector<std::unique_ptr<foo> > s;
        s.push_back(std::unique_ptr<foo>(new foo(4)));
        s.push_back(std::unique_ptr<foo>(new foo(5)));
        s.push_back(std::unique_ptr<foo>(new foo(6)));
        print(s);
    }
    
    boost::shared_ptr<> -> adopted as std::shared_ptr
    boost::thread -> adopted as std::thread
    boost::hash -> adopted (in part) as std::hash
    boost::unordered_map -> std::...
    boost::unordered_set -> std::...
    boost::lambda -> incorportated directly into the language
    boost::move -> incorportated directly into the language
    
    #include <iostream>
    #include <vector>
    
    using namespace std;
    
    template<class T>
    class pass_thru_ptr{
      T* ptr;
    public:
      T& operator*() { return *ptr; }
      pass_thru_ptr& operator=(T* p) { ptr = p; return *this; }
      //  define other operators, such as:
      //  pass_thru_ptr& operator=(T p) { *ptr = p; return *this; }
    };
    
    template<class T>
    ostream& operator<<(ostream& out, pass_thru_ptr<T> p){
      return out << (*p);
    }
    
    typedef struct udf {
      static int auto_m; int _m;
      udf(): _m(++auto_m) {}
    } * udf_ptr;
    int udf::auto_m = 0;
    
    // or template specialization?
    struct pass_thru_udf_ptr : public pass_thru_ptr<udf> {
      pass_thru_ptr<int> _m;
      pass_thru_udf_ptr& operator=(udf* p) { pass_thru_ptr<udf>::operator=(p); _m = &(p->_m); return *this; }
    };
    
    int main(int argc, char* argv[]) {
      using namespace std;
      typedef vector<pass_thru_udf_ptr> udf_p_v;
      typedef vector<udf> udf_v;
      udf_v s(1); udf_p_v p(1);
      p[0] = &s[0];
      udf_v::iterator s_i(s.begin()); udf_p_v::iterator p_i(p.begin());
    
      // Now, the indirection on p_i is not needed
      // in order to access the member data _m:
          cout
            << "s_i: " << s_i->_m << endl
            << "p_i: " << p_i->_m << endl;
    
          return 0;
    }