Обходной путь gcc при использовании shared_ptr для вставки в std::set

Этот код не компилируется с использованием g++ 4.2.1, но отлично работает с vc++ v8.

#include <set>
typedef std::set<int *> IntPtrSet;
IntPtrSet iptrSet;

typedef std::set<shared_ptr<int>> IntPtrSet2;
IntPtrSet2 iptrSet2;

void AddIntegers(int& x)
{
    iptrSet.insert(&x);
    iptrSet2.insert(&x);
}

shared_ptr похож на boost::shared_ptr или tr1::shared_ptr.

Выдает следующие ошибки,

No matching function for call to std::allocator<shared_ptr<int>>::construct(int**, const shared_ptr<int>&)
No matching function for call to std::allocator<shared_ptr<int> >::destroy(int **)

Кто-нибудь сталкивался с такой ошибкой раньше? Если да, то каков обходной путь.

Вот полное сообщение об ошибке:

/Developer/SDKs/MacOSX10.6.sdk/usr/include/c++/4.2.1/bits/stl_tree.h:402: error: no matching function for call to 'std::allocator<shared_ptr<int> >::destroy(int**)'

/Users/mark/Templates/Function/main.cpp:188:   instantiated from here
/Developer/SDKs/MacOSX10.6.sdk/usr/include/c++/4.2.1/bits/stl_tree.h:380: error: no matching function for call to 'std::allocator<shared_ptr<int> >::construct(int**, const shared_ptr<int>&)'
/Developer/SDKs/MacOSX10.6.sdk/usr/include/c++/4.2.1/ext/new_allocator.h:106: note: candidates are: void __gnu_cxx::new_allocator<_Tp>::construct(_Tp*, const _Tp&) [with _Tp = shared_ptr<int>]

Полный стек создания экземпляров шаблона:

/Developer/usr/bin/gcc-4.2 -x c++ -arch x86_64 -fmessage-length=0 -pipe -Wno-trigraphs -fpascal-strings -fasm-blocks -O0 -Wreturn-type -Wunused-variable -isysroot 
/Developer/SDKs/MacOSX10.6.sdk -mfix-and-continue -fvisibility-inlines-hidden -mmacosx-version-min=10.6 -gdwarf-2 -iquote   /Users/mark/Templates/Function/build/Function.build/Debug/Function.build/Function-generated-files.hmap - I/Users/mark/Templates/Function/build/Function.build/Debug/Function.build/Function-own-target-headers.hmap - I/Users/mark/Templates/Function/build/Function.build/Debug/Function.build/Function-all- target-headers.hmap -iquote  /Users/mark/Templates/Function/build/Function.build/Debug/Function.build/Function-project- headers.hmap -F/Users/mark/Templates/Function/build/Debug - I/Users/mark/Templates/Function/build/Debug/include - I/Users/mark/Templates/Function/build/Function.build/Debug/Function.build/DerivedSources/x86 _64 - I/Users/mark/Templates/Function/build/Function.build/Debug/Function.build/DerivedSources -c  /Users/mark/Templates/Function/main.cpp -o  /Users/mark/Templates/Function/build/Function.build/Debug/Function.build/Objects- normal/x86_64/main.o

/Developer/SDKs/MacOSX10.6.sdk/usr/include/c++/4.2.1/bits/stl_tree.h: In member function 'void std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::_M_destroy_node(std::_Rb_tree_node<_Val>*) [with _Key = shared_ptr<int>, _Val = shared_ptr<int>, _KeyOfValue = std::_Identity<shared_ptr<int> >, _Compare = std::less<shared_ptr<int> >, _Alloc = std::allocator<shared_ptr<int> >]':

/Developer/SDKs/MacOSX10.6.sdk/usr/include/c++/4.2.1/bits/stl_tree.h:1327:   instantiated from 'void std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::_M_erase(std::_Rb_tree_node<_Val>*) [with _Key = shared_ptr<int>, _Val = shared_ptr<int>, _KeyOfValue = std::_Identity<shared_ptr<int> >, _Compare = std::less<shared_ptr<int> >, _Alloc = std::allocator<shared_ptr<int> >]'

/Developer/SDKs/MacOSX10.6.sdk/usr/include/c++/4.2.1/bits/stl_tree.h:594:   instantiated from 'std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::~_Rb_tree() [with _Key = shared_ptr<int>, _Val = shared_ptr<int>, _KeyOfValue = std::_Identity<shared_ptr<int> >, _Compare = std::less<shared_ptr<int> >, _Alloc = std::allocator<shared_ptr<int> >]'

/Developer/SDKs/MacOSX10.6.sdk/usr/include/c++/4.2.1/bits/stl_set.h:141:   instantiated from 'std::set<_Key, _Compare, _Alloc>::set() [with _Key = shared_ptr<int>, _Compare = std::less<shared_ptr<int> >, _Alloc = std::allocator<shared_ptr<int> >]'

/Users/mark/Templates/Function/main.cpp:181: создан отсюда

/Developer/SDKs/MacOSX10.6.sdk/usr/include/c++/4.2.1/bits/stl_tree.h:402: error: no matching function for call to 'std::allocator<shared_ptr<int> >::destroy(int**)'

/Developer/SDKs/MacOSX10.6.sdk/usr/include/c++/4.2.1/ext/new_allocator.h:110: note: candidates are: void __gnu_cxx::new_allocator<_Tp>::destroy(_Tp*) [with _Tp = shared_ptr<int>]

/Developer/SDKs/MacOSX10.6.sdk/usr/include/c++/4.2.1/bits/stl_tree.h: In member function 'std::_Rb_tree_node<_Val>* std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::_M_create_node(const _Val&) [with _Key = shared_ptr<int>, _Val = shared_ptr<int>, _KeyOfValue = std::_Identity<shared_ptr<int> >, _Compare = std::less<shared_ptr<int> >, _Alloc = std::allocator<shared_ptr<int> >]':

/Developer/SDKs/MacOSX10.6.sdk/usr/include/c++/4.2.1/bits/stl_tree.h:840:   instantiated from 'typename std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::iterator std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::_M_insert(std::_Rb_tree_node_base*, std::_Rb_tree_node_base*, const _Val&) [with _Key = shared_ptr<int>, _Val = shared_ptr<int>, _KeyOfValue = std::_Identity<shared_ptr<int> >, _Compare = std::less<shared_ptr<int> >, _Alloc = std::allocator<shared_ptr<int> >]'

/Developer/SDKs/MacOSX10.6.sdk/usr/include/c++/4.2.1/bits/stl_tree.h:988:   instantiated from 'std::pair<typename std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::iterator, bool> std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::_M_insert_unique(const _Val&) [with _Key = shared_ptr<int>, _Val = shared_ptr<int>, _KeyOfValue = std::_Identity<shared_ptr<int> >, _Compare = std::less<shared_ptr<int> >, _Alloc = std::allocator<shared_ptr<int> >]'

/Developer/SDKs/MacOSX10.6.sdk/usr/include/c++/4.2.1/bits/stl_set.h:307:   instantiated from 'std::pair<typename std::_Rb_tree<_Key, _Key, std::_Identity<_Key>, _Compare, typename _Alloc::rebind<_Key>::other>::const_iterator, bool> std::set<_Key, _Compare, _Alloc>::insert(const _Key&) [with _Key = shared_ptr<int>, _Compare = std::less<shared_ptr<int> >, _Alloc = std::allocator<shared_ptr<int> >]'

/Users/mark/Templates/Function/main.cpp:188:   instantiated from here

/Developer/SDKs/MacOSX10.6.sdk/usr/include/c++/4.2.1/bits/stl_tree.h:380: error: no matching function for call to 'std::allocator<shared_ptr<int> >::construct(int**, const shared_ptr<int>&)'

/Developer/SDKs/MacOSX10.6.sdk/usr/include/c++/4.2.1/ext/new_allocator.h:106: note: candidates are: void __gnu_cxx::new_allocator<_Tp>::construct(_Tp*, const _Tp&) [with _Tp = shared_ptr<int>]

Вот полный код:

#include <iostream>
template <class T>
class shared_ptr
{
    private:
    T* m_p;
public:
shared_ptr() throw() : m_p(NULL){}

shared_ptr( const shared_ptr<T>& p) throw()
{
      m_p = p;
}

shared_ptr( T* p) throw()
{
    m_p = p;
}

~shared_ptr() throw()
{
    m_p = NULL;
}

T* operator=(const shared_ptr<T>& p) throw()
{
    if (m_p != p.m_p)
    {
        m_p = p;
    }
    return m_p;
}

T* operator=(T* p) throw()
{
    if (m_p != p)
    {
        m_p = p;
    }
    return m_p;
}

operator T*() const throw()
{
    return m_p;
}

T& operator*() const throw()
{
    return *m_p;
}

T** operator&() throw()
{
    return &m_p;
}

bool operator!() const throw()
{
    return (m_p == NULL);
}

bool operator<(T* p) const throw()
{
    return m_p < p;
}

bool operator!=(int nNull) const throw()
{
    return !operator==(nNull);
}

bool operator==( int nNull) const throw()
{
    return m_p == NULL;
}

bool operator!=( T* p) const throw()
{
    return !operator==(p);
}

bool operator==( T* p) const throw()
{
    return m_p == p;
}

void CopyTo( T** pp) const throw()
{
    *pp = m_p;
}

void Release() throw()
{
    T* p = m_p;
    if (p)
    {
        m_p = NULL;
    }
}

void Attach( T* p) throw()
{
    m_p = p;
}

T* Detach() throw()
{
    T* p = m_p;
    m_p = NULL;
    return p;
}

};

#include <set>
typedef std::set<int *> IntPtrSet;
IntPtrSet iptrSet;

typedef std::set<shared_ptr<int> > IntPtrSet2;
IntPtrSet2 iptrSet2;

void AddIntegers(int& x)
{
iptrSet.insert(&x);

shared_ptr<int> intPtr(new int(3));

iptrSet2.insert(intPtr);
}


int main (int argc, char * const argv[]) {


char c;
std::cin >> c;
    return 0;
}

Также на сайте Dinkumware код успешно скомпилирован. Он не компилируется с использованием g++, как упоминалось ранее. К вашему сведению, я вставил результат с веб-сайта Dinkumware (http://www.dinkumware.com/exam/default.aspx) ниже.

Ваш код был скомпилирован с помощью компилятора Microsoft Visual Studio 2005 C++ с использованием библиотеки Dinkum C++ из пакета Dinkum Compleat Libraries for VC++.

Это вывод компилятора с использованием приведенного выше кода в файле с именем sourceFile.cpp:


исходный файл.cpp


размер исходного файла.exe: 6144t + 3584 .rdata + 512d = 10240 (2800)

Код успешно скомпилирован! Сгенерированный исполняемый файл весил 11 КБ.

Большое спасибо

С уважением, Марк


person Mark    schedule 08.06.2010    source источник
comment
Было бы неплохо, если бы вы предоставили полное сообщение об ошибке, включая стек создания экземпляров шаблона.   -  person jpalecek    schedule 08.06.2010


Ответы (4)


Ваша реализация shared_ptr кажется очень необычной.

По сравнению с boost/tr1 у вас есть дополнительные операторы преобразования для T** и T*. Неявное преобразование в базовый тип указателя (T*) только запутает компилятор.

После того, как я закомментировал эти методы, мне пришлось исправить конструктор копирования, чтобы он действительно копировал элементы, а не злоупотреблял оператором преобразования. Он по-прежнему не будет работать как общий указатель, потому что нет подсчета ссылок.

Затем мне пришлось исправить operator<, чтобы снова взять const shared_ptr<T>& p, потому что неявные преобразования заставляли это компилироваться, но позже сбивали с толку компилятор std::set. Он запутался, потому что пытался создать shared_ptr, но operator& (я думаю) заставил его ухудшиться до T**, поэтому тип не соответствовал типу внутри контейнера.

С этими изменениями я смог успешно скомпилировать его с помощью g++ 4.2.

РЕДАКТИРОВАТЬ: Ну, я смог написать собственный распределитель, который компилируется, но он не кажется очень чистым и может даже не работать во всех случаях.

template <class T>
class sh_ptr_alloc : public std::allocator<T>
{
public:
        typedef size_t     size_type;

      sh_ptr_alloc() throw() { }

      template<typename Tp1>
        sh_ptr_alloc(const sh_ptr_alloc<Tp1>&) throw() { }

      template<typename Tp1>
        struct rebind
        { typedef sh_ptr_alloc<Tp1> other; };

template <class Tp1>
      void
      deallocate(Tp1* p, size_type)
      { ::operator delete(p); }

template <class Tp1>
      void 
      construct(Tp1** p, const shared_ptr<Tp1>& val) 
      { ::new(p) shared_ptr<int>(val); }

template <class Tp1>
      void 
      destroy(Tp1** p) { }
};

Тогда набор такой:

typedef std::set<shared_ptr<int>, std::less<shared_ptr<int> >, sh_ptr_alloc<shared_ptr<int> > > IntPtrSet2;

person Mark B    schedule 08.06.2010
comment
Большое спасибо за ваше решение. :) Проблема в том, что Shared_ptr, который я использую, является частью кода, на изменение которого у меня нет прав. Много другого клиентского кода зависит от операторов неявного преобразования. Есть ли другой обходной путь без изменения shared_ptr? - person Mark; 09.06.2010
comment
@Марк понял. Но также подумайте: если большая часть кода зависит от неявных преобразований, подумайте, сколько кода может быть сломано по незнанию, потому что неявные преобразования вызываются неожиданным образом? - person Mark B; 09.06.2010
comment
Согласовано. Я буду настаивать на использовании лучшего shared_ptr. Спасибо за ваше решение с использованием пользовательского распределителя. :) - person Mark; 09.06.2010

По (очевидной) причине неявное преобразование T* в shared_ptr<T> отсутствует.

Например, следующий код не работает:

int i = 3;
shared_ptr<int> ptr;
ptr = &i;

Для этого есть веская причина: shared_ptr ВЛАДЕЕТ ресурсами, на которые он указывает. Вы не можете вслепую кинуть что-то в shared_ptr, чтобы получить бесплатное управление памятью, вы должны убедиться, что объект уже не принадлежит чему-то другому.

Решение состоит в том, чтобы использовать либо конструктор, либо построитель:

shared_ptr<int> ptr(new int(3));

shared_ptr<int> ptr = shared_ptr<int>(new int(3));

shared_ptr<int> made = make_shared<int>(3);

Последний метод приводит к несколько более компактному указателю, хотя разница вряд ли затронет вас.

Я хотел бы подчеркнуть, что ваш пример подозрительный. Право собственности лучше применять прямо во время создания. В такой функции есть вероятность, что кто-то другой уже приобрел право собственности, и вы создаете Большую Плохую Ошибку.

person Matthieu M.    schedule 08.06.2010
comment
Я пробовал это, но все равно получаю то же сообщение об ошибке. Я разместил точное сообщение об ошибке. - person Mark; 08.06.2010

Похоже, у него проблемы с преобразованием из int* в shared_ptr. Я думаю, обходной путь довольно прост:

shared_ptr<int> intPtr(&x);
iptrSet2.insert(intPtr);
person bshields    schedule 08.06.2010

void AddIntegers(int& x)
{
...
    iptrSet2.insert(&x);

Это, вероятно, не работает, потому что нет неявного преобразования формы int* в boost::shared_ptr<int>. Вам придется построить его вручную (и будьте осторожны: для одного объекта может быть создан только один shared_ptr; другие указатели должны быть созданы как копии этого одного «главного» общего указателя).

person jpalecek    schedule 08.06.2010
comment
Я пробовал это, но все равно не получается. Выложил полное сообщение об ошибке и стек. Благодарность - person Mark; 08.06.2010