我在const和非const类方法上考虑this question。首选答案取自Scott Meyers的Effective C ++,其中非const方法是根据const方法实现的。
进一步扩展,如果方法返回迭代器而不是引用,如何减少代码重复?修改链接问题中的示例:
class X
{
std::vector<Z> vecZ;
public:
std::vector<Z>::iterator Z(size_t index)
{
// ...
}
std::vector<Z>::const_iterator Z(size_t index) const
{
// ...
}
};
我无法在const方法方面实现非const方法,因为不使用distance()/ advance()技术就无法直接从const_iterator转换为迭代器。
在这个例子中,因为我们使用std :: vector作为容器,所以实际上可以从const_iterator转换为迭代器,因为它们很可能被实现为指针。我不想依赖于此。有更普遍的解决方案吗?
答案 0 :(得分:5)
你可以使用几种技巧。 You can turn a const_iterator into an iterator if you have non-const access to the container.(你这样做):
std::vector<Z>::iterator Z(size_t index)
{
std::vector<Z>::const_iterator i = static_cast<const X&>(*this).Z(index);
return vecZ.erase(i, i);
}
你也可以这样做:
std::vector<Z>::iterator Z(size_t index)
{
return std::next(vecZ.begin(), std::distance(vecZ.cbegin(), static_cast<const X&>(*this).Z(index)));
}
两者都不是特别优雅。为什么我们没有像const_iterator_cast
这样的const_pointer_cast
?也许我们应该。
编辑,我错过了最明显和最优雅的解决方案,因为我试图使用非const方法的const方法。在这里我做了相反的事情:
std::vector<Z>::const_iterator Z(size_t index) const
{
return const_cast<X&>(*this).Z(index);
}
您正在取消引用const_cast
的结果,但只要非const Z
不修改X
中的任何数据,这不是未定义的行为。与我给出的其他两个解决方案不同,这是我可能在实践中使用的解决方案。
答案 1 :(得分:2)
我相信,只有助手
才有可能typedef int Z;
class X
{
std::vector<Z> vecZ;
public:
std::vector<Z>::iterator foo(size_t index)
{
return helper(*this);
}
std::vector<Z>::const_iterator foo(size_t index) const
{
return helper(*this);
}
template <typename T>
static auto helper(T& t) -> decltype(t.vecZ.begin())
{
return t.vecZ.begin();
}
};
修改强> 可以在没有c ++ 11的情况下实现相同的
template <typename T>
struct select
{
typedef std::vector<Z>::iterator type;
};
template <typename T>
struct select<const T&>
{
typedef std::vector<Z>::const_iterator type;
};
template <typename T>
static
typename select<T>::type
helper(T t)
{
return t.vecZ.begin();
}
但是,我认为你在使用这个approcach之前应该三思而后
答案 2 :(得分:1)
如果您正在使用C ++ 11,那么有一种更优雅的(IMHO)方式,它在模板迭代器类中仅使用两个typedef来区分const和常规迭代器: http://www.sjvs.nl/c-implementing-const_iterator-and-non-const-iterator-without-code-duplication/