std :: auto_ptr Проблема компиляции в Visual Studio 6.0 - PullRequest
2 голосов
/ 17 июля 2009

Обновление: отредактированный пример кода для использования AutoA для обходного пути (который был первоначальным намерением). Понял это, увидев ответ rlbond.

Я пытаюсь включить использование auto_ptr в мой код на основе рекомендаций из этой темы:

Выразите использование аргументов C ++ через интерфейсы методов

Однако при компиляции с Visual Studio 6.0 я получаю некоторые неожиданные ошибки компиляции. Это имеет проблему при работе с присваиваниями / копиями std::auto_ptr производного типа для std::auto_ptr базового типа. Это проблема для моего компилятора?

Я знаю, что есть сильная рекомендация использовать Boost, но в моем проекте это не вариант. Если я все еще хочу использовать auto_ptr, вынужден ли я использовать обходной путь вызова std::auto_ptr::release()? Из того, с чем я столкнулся до сих пор, эта проблема приводит к ошибке компилятора, так что ее достаточно легко обнаружить. Однако, может ли принятие соглашения о вызове release для присвоения auto_ptr базового типа повлечь за собой какие-либо проблемы с обслуживанием? Особенно если он собран с другим компилятором (при условии, что другие компиляторы не имеют этой проблемы).

Если обходной путь release() не подходит из-за моих обстоятельств, должен ли я использовать другое соглашение для описания передачи права собственности?

Ниже приведен пример, иллюстрирующий проблему.

#include "stdafx.h"
#include <memory>

struct A
{
    int x;
};

struct B : public A
{
    int y;
};

typedef std::auto_ptr<A> AutoA;
typedef std::auto_ptr<B> AutoB;

void sink(AutoA a)
{
    //Some Code....
}

int main(int argc, char* argv[])
{
    //Raws to auto ptr
    AutoA a_raw_to_a_auto(new A());
    AutoB b_raw_to_b_auto(new B());
    AutoA b_raw_to_a_auto(new B());

    //autos to same type autos
    AutoA a_auto_to_a_auto(a_raw_to_a_auto);
    AutoB b_auto_to_b_auto(b_raw_to_b_auto);

    //raw derive to auto base
    AutoB b_auto(new B());

    //auto derive to auto base
    AutoA b_auto_to_a_auto(b_auto);  //fails to compile

    //workaround to avoid compile error.
    AutoB b_workaround(new B());
    AutoA b_auto_to_a_auto_workaround(b_workaround.release());

    sink(a_raw_to_a_auto);
    sink(b_raw_to_b_auto);  //fails to compile

    return 0;
}

Ошибка компиляции:

Compiling...
Sandbox.cpp
C:\Program Files\Microsoft Visual Studio\MyProjects\Sandbox\Sandbox.cpp(40) : error C2664: '__thiscall std::auto_ptr<struct A>::std::auto_ptr<struct A>(struct A *)' : cannot convert parameter 1 from 'class std::auto_ptr<struct B>' to 'struct A *'
        No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called
C:\Program Files\Microsoft Visual Studio\MyProjects\Sandbox\Sandbox.cpp(47) : error C2664: 'sink' : cannot convert parameter 1 from 'class std::auto_ptr<struct B>' to 'class std::auto_ptr<struct A>'
        No constructor could take the source type, or constructor overload resolution was ambiguous
Error executing cl.exe.

Sandbox.exe - 2 error(s), 0 warning(s)

Ответы [ 3 ]

5 голосов
/ 17 июля 2009

Первый прост:

AutoA b_auto_to_a_auto(b_auto);  //fails to compile

Это не работает на VC6, поскольку для него требуется шаблоны функций-членов , что стандартная библиотека VC6 не поддерживает. Он компилируется на стандартных совместимых компиляторах.

Обход:

AutoA b_auto_to_a_auto( b_auto.release() );

Второй гораздо более тонкий:)

sink(b_raw_to_b_auto);  //fails to compile

Этот не должен компилироваться на совместимом со стандартами компиляторе, потому что происходит неявное преобразование. Компилятор превращает вышеприведенное в

sink( std::auto_ptr<A>( b_raw_to_b_auto ) );

однако, sink принимает std::auto_ptr<A> на значение , поэтому временное std::auto_ptr<A>, созданное неявно компилятором, должно быть copy-constructed в аргумент sink. Теперь такие временные значения rvalues ​​. R-значения не привязываются к неконстантным ссылкам, но «конструктор копирования» std::auto_ptr получает аргумент по неконстантной ссылке.

Вот и все - ошибка компиляции. AFAICS это поведение, соответствующее стандартам. C ++ - 0x «семантика перемещения» исправит это, добавив «конструктор копирования», который принимает ссылку на rvalue, хотя я не уверен, сколько любви std::auto_ptr все еще получит в будущем, что с std::shared_ptr и всеми.

Обходной путь для второго:

AutoA tmp( b_raw_to_b_auto/*.release() for VC6*/ );
sink( tmp );
4 голосов
/ 17 июля 2009
AutoA b_auto_to_a_auto(b_auto);  //fails to compile

sink(b_raw_to_b_auto);  //fails to compile

Павел Минаев указывает на то, чего я на самом деле не знал:

Первый вызов должен скомпилироваться, потому что существует неявное преобразование из B * в A *. Однако второе не скомпилируется. Следующее будет:

sink(static_cast<AutoA>(b_raw_to_b_auto));

VC6 известен тем, что он не очень хорош с шаблонами.

Я настоятельно рекомендую вам обновить базу кода до действующей и начать использовать методы RAII, особенно boost::shared_ptr. Я знаю, что вы говорите, что не можете, и я знаю, что это сложно, но у вас практически не будет утечек памяти и много-много ошибок.

Опять же, может быть, даже без полной функциональности вы можете использовать auto_ptr?

2 голосов
/ 17 июля 2009

Здесь есть две проблемы. Прежде всего это:

AutoA b_auto_to_a_auto(b_auto);  

Он полностью соответствует стандарту и должен компилироваться. Позвольте мне объяснить, почему. Стандарт ISO C ++ определяет (20.4.5.1 [lib.auto.ptr.cons] / 4-6) следующий конструктор для auto_ptr<X> (среди прочих);

template<class Y> auto_ptr(auto_ptr<Y>&) throw();

Обратите внимание, что Y отличается от X здесь. Кроме того, Стандарт гласит:

Требуется: Y * может быть неявно преобразовано в X *.

Единственное, на что следует обратить внимание, это то, что аргумент конструктора является ссылкой на неконстантный. Для вашего случая это не проблема (поскольку вы передаете туда неконстантную переменную), но это становится важным для следующей части. В заключение: вы видите нестандартное поведение в VC6. Он должен компилироваться на совместимом компиляторе (и будет компилироваться на VC7 и выше). Теперь перейдем ко второму:

sink(b_raw_to_b_auto);  //fails to compile

Это на самом деле прекрасно объясняется Ммуцем, поэтому я не буду вдаваться в подробности здесь - см. Его ответ. В заключение: да, эта строка не должна компилироваться и не будет в совместимом компиляторе (или VC6, как вы узнали).

...