簡體   English   中英

如何刪除 const_iterator 的常量性?

[英]How to remove constness of const_iterator?

作為這個問題的擴展Are const_iterators faster? ,我還有一個關於const_iterators的問題。 如何刪除const_iterator的常量性? 盡管迭代器是指針的通用形式,但const_iteratoriterator仍然是兩個不同的東西。 因此,我相信,我也不能使用const_cast<>const_iterator轉換為iterator s。

一種方法可能是您定義一個迭代器,它移動直到const_iterator指向的元素。 但這看起來是一個線性時間算法。

關於什么是實現此目標的最佳方法的任何想法?

在 C++11 中有一個具有恆定時間復雜度的解決方案:對於任何序列、關聯或無序關聯容器(包括所有標准庫容器),您可以使用空范圍調用范圍擦除成員函數:

template <typename Container, typename ConstIterator>
typename Container::iterator remove_constness(Container& c, ConstIterator it)
{
    return c.erase(it, it);
}

range-erase 成員函數有一對const_iterator參數,但它們返回一個iterator 因為提供了一個空范圍,所以對erase 的調用不會改變容器的內容。

向霍華德·辛南特和喬恩·卡爾布致敬這個技巧。

不幸的是,線性時間是唯一的方法:

iter i(d.begin());
advance (i,distance<ConstIter>(i,ci));

其中 iter 和 constIter 是合適的 typedef, d 是您正在迭代的容器。

在您上一篇文章的答案中,有幾個人(包括我在內)出於與性能無關的原因推薦使用 const_iterators。 可讀性、從設計板到代碼的可追溯性……使用 const_iterators 提供對非常量元素的可變訪問比根本不使用 const_iterators 要糟糕得多。 您正在將您的代碼轉換成只有您才能理解的東西,設計更糟糕,可維護性也很痛苦。 僅僅使用 const 來拋棄它比根本不使用 const 更糟糕。

如果你確定你想要它,C++ 的好/壞部分是你總能得到足夠的繩子來吊死自己。 如果您打算使用 const_iterator 來解決性能問題,那么您真的應該重新考慮一下,但是如果您仍然想放手一搏……那么 C++ 可以提​​供您選擇的武器。

首先,最簡單的:如果您的操作將參數作為常量(即使在內部應用 const_cast),我相信它應該在大多數實現中直接工作(即使它可能是未定義的行為)。

如果你不能改變函子,那么你可以從任何一方解決這個問題:在 const 迭代器周圍提供一個非常量迭代器包裝器,或者在非常量函子周圍提供一個 const 函子包裝器。

迭代器外觀,漫漫長路:

template <typename T>
struct remove_const
{
    typedef T type;
};
template <typename T>
struct remove_const<const T>
{
    typedef T type;
};

template <typename T>
class unconst_iterator_type
{
    public:
        typedef std::forward_iterator_tag iterator_category;
        typedef typename remove_const<
                typename std::iterator_traits<T>::value_type
            >::type value_type;
        typedef value_type* pointer;
        typedef value_type& reference;

        unconst_iterator_type( T it )
            : it_( it ) {} // allow implicit conversions
        unconst_iterator_type& operator++() {
            ++it_;
            return *this;
        }
        value_type& operator*() {
            return const_cast<value_type&>( *it_ );
        }
        pointer operator->() {
            return const_cast<pointer>( &(*it_) );
        }
        friend bool operator==( unconst_iterator_type<T> const & lhs,
                unconst_iterator_type<T> const & rhs )
        {
            return lhs.it_ == rhs.it_;
        }
        friend bool operator!=( unconst_iterator_type<T> const & lhs,
                unconst_iterator_type<T> const & rhs )
        {
            return !( lhs == rhs );
        }
    private:
        T it_;  // internal (const) iterator
};

Scott Meyer關於首選迭代器而不是 const_iterators的文章回答了這個問題。 Visage 的答案是唯一安全的 pre-C++11 替代方案,但實際上是實現良好的隨機訪問迭代器的恆定時間,而其他的則是線性時間。

這可能不是您想要的答案,但有些相關。

我假設你想改變迭代器指向的東西。 我做的最簡單的方法是 const_cast 返回的引用。

像這樣的東西

const_cast<T&>(*it);

我相信在設計良好的程序中不需要這種轉換。

如果您需要這樣做 - 嘗試重新設計代碼。

作為解決方法,您可以使用以下方法:

typedef std::vector< size_t > container_type;
container_type v;
// filling container code 
container_type::const_iterator ci = v.begin() + 3; // set some value 
container_type::iterator i = v.begin();
std::advance( i, std::distance< container_type::const_iterator >( v.begin(), ci ) );

但我認為有時這種轉換是不可能的,因為您的算法無法訪問容器。

您可以從 const_iterator 中減去 begin() 迭代器以獲得 const_iterator 指向的位置,然后將 begin() 添加回該位置以獲得非常量迭代器。 我認為這對於非線性容器不是很有效,但是對於線性容器(例如向量),這將花費恆定的時間。

vector<int> v;                                                                                                         
v.push_back(0);
v.push_back(1);
v.push_back(2);
v.push_back(3);
vector<int>::const_iterator ci = v.begin() + 2;
cout << *ci << endl;
vector<int>::iterator it = v.begin() + (ci - v.begin());
cout << *it << endl;
*it = 20;
cout << *ci << endl;

編輯:這似乎只適用於線性(隨機訪問)容器。

我認為想出一個解決方案來解決這個問題會很有趣,該解決方案適用於不在標准庫中且不包含 erase() 方法的容器。

嘗試使用它會導致 Visual Studio 2013 在編譯時掛起。 我不包括測試用例,因為把它留給可以快速弄清楚界面的讀者似乎是個好主意; 我不知道為什么這會掛在編譯上。 即使 const_iterator 等於 begin(),也會發生這種情況。

// deconst.h

#ifndef _miscTools_deconst
#define _miscTools_deconst

#ifdef _WIN32 
    #include <Windows.h>
#endif

namespace miscTools
{
    template < typename T >
    struct deconst
    {

        static inline typename T::iterator iterator ( typename T::const_iterator*&& target, T*&& subject )
        {
            typename T::iterator && resultant = subject->begin ( );

            bool goodItty = process < 0, T >::step ( std::move ( target ), std::move ( &resultant ), std::move ( subject ) );

        #ifdef _WIN32
             // This is just my habit with test code, and would normally be replaced by an assert
             if ( goodItty == false ) 
             {
                  OutputDebugString ( "     ERROR: deconst::iterator call. Target iterator is not within the bounds of the subject container.\n" ) 
             }
        #endif
            return std::move ( resultant );
        }

    private:

        template < std::size_t i, typename T >
        struct process
        {
            static inline bool step ( typename T::const_iterator*&& target, typename T::iterator*&& variant, T*&& subject )
            {
                if ( ( static_cast <typename T::const_iterator> ( subject->begin () + i ) ) == *target )
                {
                    ( *variant ) += i;
                    return true;
                }
                else
                {
                    if ( ( *variant + i ) < subject->end () )
                    {
                        process < ( i + 1 ), T >::step ( std::move ( target ), std::move ( variant ), std::move ( subject ) );
                    }
                    else { return false; }
                }
            }
        };
    };
}

#endif

假設您的容器的const_iterator具有與其iterator相同的布局(對所有 STL 容器的有效假設),您可以簡單地將前者位轉換為后者:

#include <bit>
#include <vector>

void demo() {
    using vec_t = std::vector<int>;
    vec_t v { 1, 2, 3 };
    vec_t::const_iterator c_it = v.cbegin();
    vec_t::iterator it = std::bit_cast<vec_t::iterator>(c_it);
    *it = 4; // OK, now v holds { 4, 2, 3 }
}

您可以將 const 迭代器值指針轉換為非 const 值指針並直接使用它,如下所示

    vector<int> v;                                                                                                         
v.push_back(0);
v.push_back(1);
v.push_back(2);
v.push_back(2);
vector<int>::const_iterator ci = v.begin() + 2;
cout << *ci << endl;
*const_cast<int*>(&(*ci)) = 7;
cout << *ci << endl;

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM