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
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;
}