0

I have a Holder class that should expose only the values of its map. I used a transform iterator to do so. It works if I use plain pointer to the type (ClassA*) but not with unique_ptr. I based the following example on this code: transform_iterator compile problem

I'm getting the following error (originating in the begin() function), which let's me think that someone is trying to copy the pair instead of using a reference.

Thanks in advance

error C2248:
'std::unique_ptr<ClassA,std::default_delete<_Ty>>::unique_ptr' :
cannot access private member declared in class
'std::unique_ptr<ClassA,std::default_delete<_Ty>>'

#include <iostream>
#include <map>
#include <functional>
#include <memory>
#include <string>
#include <boost/iterator/transform_iterator.hpp>

struct ClassA
{
    ClassA( const std::string& strName ) : m_strName( strName ) {}
    std::string m_strName;
};

template <typename K, typename V>
const V & get_value(std::pair<K, V> const & p)  { return p.second; }

class Holder
{
    typedef std::map<int, std::unique_ptr< ClassA > > TMap;
    typedef std::unique_ptr< ClassA > UniqueA;
    TMap m_Map;
public:
    Holder()
    {
        UniqueA a( new ClassA( "#2# ") );
        UniqueA b( new ClassA( "#3# ") );
        UniqueA c( new ClassA( "#4# ") );
        m_Map.insert( std::make_pair( 2, std::move( a ) ) );
        m_Map.insert( std::make_pair( 3, std::move( b ) ) );
        m_Map.insert( std::make_pair( 4, std::move( c ) ) );
    }
    typedef std::function< const TMap::mapped_type & (const TMap::value_type &) > F;
    typedef boost::transform_iterator<F, TMap::iterator> transform_iterator;

    transform_iterator begin()
    {
        return boost::make_transform_iterator(m_Map.begin(), &get_value< int, std::unique_ptr< ClassA > >);
    }
    transform_iterator end()
    {
        return boost::make_transform_iterator(m_Map.end(), &get_value< int, std::unique_ptr< ClassA > >);
    }
};

void MyTest()
{
    Holder bla;
    auto s_beg = bla.begin();
    auto s_end = bla.end();
    for( auto t=s_beg; t!=s_end;++t) {
        std::cout << ( *t )->m_strName << std::endl;
    }
}
Community
  • 1
  • 1

1 Answers1

2

The problem is that get_value takes a reference to pair<K,V>, but is being passed a reference to the map's value type, pair<const K,V>. This requires a conversion, which requires copying both the key and the value to a new pair. You get the error because unique_ptr can't be copied.

Solution 1: change get_value to accept a reference to pair<const K,V> to match TMap::value_type. This allows the function to accept references to the map's values directly.

Solution 2: Instantiate get_value for const int rather than int; this will have the same effect as solution 1.

Solution 3: Instantiate get_value for const unique_ptr & rather than unique_ptr, so that it takes a pair containing a reference to the map's value. This temporary pair can be created without copying the unique_ptr.

Mike Seymour
  • 249,747
  • 28
  • 448
  • 644