Removing code duplication from const and non-const methods that return iterators

点点圈 提交于 2019-12-10 02:25:19

问题


I'm considering this question on const and non-const class methods. The preferred answer is taken from Effective C++ by Scott Meyers, where the non-const method is implemented in terms of the const method.

Extending this further, how can code duplication be reduced if the methods return iterators instead of references? Modifying the example in the linked question:

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
    {
        // ...
    }
};

I can't implement the non-const method in terms of the const method because it's not possible to convert directly from a const_iterator to an iterator without using the distance()/advance() technique.

In the example, because we're using a std::vector as the container, it might actually be possible to cast from a const_iterator to an iterator because they may well be implemented as pointers. I don't want to rely on this. Is there a more general solution?


回答1:


I believe, it is only possible with the helper

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

EDIT Same can be implemented without 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();
}

But, well, I think you should think twice before using this approcach




回答2:


There are a couple tricks you can use. You can turn a const_iterator into an iterator if you have non-const access to the container. (which you do):

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

You could also do this:

std::vector<Z>::iterator Z(size_t index)
{
    return std::next(vecZ.begin(), std::distance(vecZ.cbegin(), static_cast<const X&>(*this).Z(index)));
}

Neither are particularly elegant. Why don't we have a const_iterator_cast like const_pointer_cast? Maybe we should.

EDIT, I missed the most obvious and elegant solution because I was trying to use the const method from the non-const method. Here I do the opposite:

std::vector<Z>::const_iterator Z(size_t index) const
{
    return const_cast<X&>(*this).Z(index);
}

You are dereferencing the result of const_cast however this is not undefined behavior as long as the non-const Z does not modify any data in X. Unlike the other 2 solutions I gave, this is one I might use in practice.




回答3:


If you're using C++11, there is a more elegant (IMHO) way that uses only two typedefs in a template iterator class to distinguish between a const and regular iterator: http://www.sjvs.nl/c-implementing-const_iterator-and-non-const-iterator-without-code-duplication/



来源:https://stackoverflow.com/questions/14446058/removing-code-duplication-from-const-and-non-const-methods-that-return-iterators

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!