//Cat.h
class Cat
{public:
void const_meow() const{ ... };
void meow(){ ... };
};
class CatLibrary
{public:
std::vector<std::shared_ptr<Cat>>::iterator begin()
{
return m_cat_list.begin();
}
// compile error, the compiler complains cannot covert type
// from `std::vector<std::shared_ptr<Cat>>::const_iterator`
// to `std::vector<std::shared_ptr<const Cat>>::const_iterator`
std::vector<std::shared_ptr<const Cat>>::const_iterator begin() const
{
return m_cat_list.cbegin();
}
private:
std::vector<std::shared_ptr<Cat>> m_cat_list;
};
//main.cpp CatLibrary cat_library;
cat_library.add(std::make_shared<Cat>());
cat_library.add(std::make_shared<Cat>());
for(auto& cat: cat_library )
{
cat->const_meow();
cat->meow();
}
for(const auto& cat: cat_library)
{
cat->const_meow();
cat->meow(); // hope to compile error due to invoking non_const method of Cat.
}
const CatLibrary& const_cat_library = cat_library;
for(auto& cat: const_cat_library )
{
cat->const_meow();
cat->meow(); // hope to compile error due to invoking non_const method of Cat.
}
const CatLibrary& const_cat_library = cat_library;
for(const auto& cat: const_cat_library )
{
cat->const_meow();
cat->meow(); // hope to compile error due to invoking non_const method of Cat.
}
我希望我的CatLibrary公开non-const begin()
和non-const end()
,其中客户端可以迭代指向可变Cat的智能指针。const begin()
和const end()
返回指向不可变迭代器的迭代器
那么当客户端迭代const CatLibrary时,我就不会担心他会修改库中Cat的内容。
但是添加到我的成员函数begin()
中的const
只限定该指针为const指针,而不限定它所指向的Cat。
如果不使用指针,则具有constness的vector使迭代器也指向具有constness的元素。但是我希望这个效果也适用于智能指针指向的元素。
我有一个方法来解决我的问题,但我不确定在未来的使用中会发生什么问题。
在const和nonconst
中维护两个向量#include <iostream>
#include <memory>
#include <vector>
class Cat
{public:
void const_meow() const { std::cout << "meow" << std::endl;}
void meow() { std::cout << "meow" << std::endl;}
};
class CatLibrary
{public:
void add(std::shared_ptr<Cat> cat)
{
m_cat_list.push_back(cat);
m_cat_const_list.push_back(cat);
};
std::vector<std::shared_ptr<Cat>>::const_iterator begin()
{
return m_cat_list.begin();
}
std::vector<std::shared_ptr<const Cat>>::const_iterator begin() const
{
return m_cat_const_list.begin();
}
std::vector<std::shared_ptr<Cat>>::const_iterator end()
{
return m_cat_list.end();
}
std::vector<std::shared_ptr<const Cat>>::const_iterator end() const
{
return m_cat_const_list.end();
}
private:
std::vector<std::shared_ptr<Cat>> m_cat_list;
std::vector<std::shared_ptr<const Cat>> m_cat_const_list;
};
int main()
{
CatLibrary cat_library;
cat_library.add(std::make_shared<Cat>());
cat_library.add(std::make_shared<Cat>());
cat_library.add(std::make_shared<Cat>());
const CatLibrary& const_cat_library = cat_library;
for(auto& cat: cat_library)
{
cat->meow();
}
return 0;
}
或者是否有另一种方法来解决向量中智能指针的这种constness问题?
在您发布的示例中,不需要begin()和end()的const版本。你可以在基于range的for循环中使用const引用,而不需要这些函数的const版本,你也不需要将cat设置为const auto&调用const成员函数
如果cat_library对象本身是const,则可能需要const begin()和end(),但这样就不能添加这样的项了
我实际上会考虑改变抽象并封装您的猫集合。迭代器和可变对象是实现细节。
所以用cat来写函数:
PrintCats()
PlayWithCats()
如果你的cats库不知道你想对它们执行的操作,你可以考虑传入函数指针。Boost::函数可以很好地解决这个问题。你会有一个像
这样的函数void CatLibrary::DoStuffToCats(boost::function<void, (const Cat&)> f))
{
std::foreach(m_cat_list.begin(), m_cat_list.end(), f);
}
我用boost::transform_iterator玩了一下,似乎可以实现你想要的,尽管我发现结果并不令人满意。我想丹丹建议不向用户公开实现细节可能是正确的方法。
尽管如此,这里是我的机会boost::transform_iterator作为引用:
#include <boost/iterator/transform_iterator.hpp>
class CatLibrary
{
public:
typedef std::vector<std::shared_ptr<Cat>> cat_list_t;
typedef std::function<const Cat *(const std::shared_ptr<Cat>&)> transform_t;
typedef boost::transform_iterator<transform_t,
cat_list_t::const_iterator> const_iterator;
[...]
const_iterator begin() const {
return const_iterator(std::begin(m_cat_list),
[](const std::shared_ptr<Cat>& c) {
return static_cast<const Cat *>(c.get());
});
}
const_iterator end() const { // same as above ... }
};
使用const_cat_library
的for循环中的cat
类型现在是const Cat*
,因此不允许调用非const函数。使用g++ -c --std=c++11 transform_iterator.cxx -Wall -I$BOOSTINC
(gcc版本4.8.1)编译会产生以下错误:
error: invalid initialization of non-const reference of type 'const Cat*&' from an rvalue of type 'boost::[...]::reference {aka const Cat*}'
for(auto& cat: const_cat_library ) {
^
//twice:
error: passing 'const Cat' as 'this' argument of 'void Cat::meow()' discards qualifiers [-fpermissive]
cat->meow(); // hope to compile error due to invoking non_const method of Cat.
^
可能出现的问题之一是shared_ptr被绕过,用户可以在循环中删除Cat
对象。