2013-01-21 77 views
9

我正在考虑在const和非const类方法上使用this question。首选答案取自Scott Meyers的Effective C++,其中非const方法以const方法实现。从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的方法方面,因为它不可能直接从常量性转换为迭代器不使用的距离()/advance()技术。

在这个例子中,因为我们使用std :: vector作为容器,实际上可能从const_iterator转换为迭代器,因为它们很可能被实现为指针。我不想依赖这个。有更通用的解决方案吗?

+0

该函数要么是微不足道的或应该被实现为一个算法函数模板,使成员函数再次平凡无奇。 – ipc

+0

在这种情况下,这两个函数都可以是'return vecZ.begin()+ index'。或者,如果你绝对必须让它们不同,那么const版本可以是'return vecZ.cbegin()+ index'。看起来,没有重复! –

回答

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

但是,好了,我想你应该可以实现在使用本产品之前请三思

+0

这看起来像一个有用的方法,虽然在可预见的未来我的目标平台上没有C++ 11编译器。 –

+1

@SimonElliott,请参阅编辑 – Lol4t0

+0

这与[三天前的尝试](http://stackoverflow.com/questions/14401017/call-nonconst-member-version-from-const)具有相同的问题以减少代码重复 - 它实际上导致更多的代码。优势在哪里? –

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_castconst_pointer_cast?也许我们应该。

编辑,我错过了最明显和优雅的解决方案,因为我试图从非const方法使用const方法。在这里,我却反其道而行之:

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

您提领的const_cast结果但这并不是不确定的行为,只要非const ZX修改任何数据。与我给出的其他两种解决方案不同,这是我可能在实践中使用的解决方案。

+1

你应该总是将const版本和const抛弃掉。这可以防止实现意外地改变这一点。 –

+0

@CharlesBeattie防止意外变更'this'的执行?它可以防止_you_在非const方法中意外地突变'this'。但是,我已经写了这个免责声明。如果你修改非const的'Z'中的'X'并且从const'Z'调用非const'Z',那么它就是UB。我同意这是不好的做法,尤其是如果人们正在搞乱'Z'并且有人可能会为它添加一些修改代码。 – David

+1

是的,我认为你的想法反映了我的这一点。我想增加额外的重点,最后的解决方案是不好的做法(尽管这是一个实际的解决方案)。 –

相关问题