Почему auto_ptr нарушает частное наследование в Visual C ++?

Справочная информация: это было обнаружено в Visual Studio 2008 и снова подтверждено в Visual Studio 2013. G ++ закричал в коде, в то время как Visual молча принял частное нарушение наследования.

Итак, на Visual C ++ у нас есть следующий код:

class Base {};
class Derived : Base {};      // inherits privately. Adding explicitly the
//    keyword private changes nothing

int main()
{
std::auto_ptr<Base>(new Derived) ;   // compiles, which is NOT EXPECTED
std::auto_ptr<Base> p(new Derived) ; // Does not compile, which is expected
}

Почему первый (временный) auto_ptr компилируется? Я вошел в него в отладке, он сделал именно то, что должен был сделать с публичным наследованием (вызов правильного конструктора и т. Д.)

Интересно, возможно ли проблема была в реализации auto_ptr (мы никогда не узнаем …), я уменьшил проблему в этом автономном коде:

class Base {};
class Derived : Base {};

template <typename T>
class Ptr
{
T * m_p;

public :
Ptr(T * p_p)
: m_p(p_p)
{
}
} ;

int main()
{
Ptr<Base>(new Derived) ;   // compiles, which is NOT EXPECTED
Ptr<Base> p(new Derived) ; // Does not compile, which is expected
}

Опять же, я ожидал, что код НЕ скомпилируется, поскольку Derived наследуется от Base.

Но когда мы создаем временный, это работает.

И мы не можем винить в этом std :: auto_ptr.

Что-то в стандарте (или 98, или 11, или 14) я пропустил, или это ошибка?

14

Решение

Derived*-До-Base* преобразование, даже если наследование является частным, разрешено в C-стиле и функциональных приведениях. И нет, это не значит, reinterpret_cast в таком случае.

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

5.2.3 Явное преобразование типов (функциональная запись) [expr.type.conv]

1 […] Если список выражений является одним выражением, выражение преобразования типа эквивалентно (в определенности и если определено в значении) соответствующему приведенному выражению (5.4). […]

5.4 Явное приведение типов (нотация приведения) [expr.cast]

4 Преобразования, выполненные

  • const_cast (5.2.11),
  • static_cast (5.2.9),
  • static_cast с последующим const_cast,
  • reinterpret_cast (5.2.10) или
  • reinterpret_cast с последующим const_cast,

может выполняться с использованием приведенной нотации явного преобразования типов. Применяются те же семантические ограничения и поведение, за исключением того, что при выполнении
static_cast в следующих ситуациях преобразование допустимо, даже если базовый класс недоступен:

  • указатель на объект производного типа класса или lvalue или rvalue производного типа класса может быть явно
    преобразуется в указатель или ссылку на однозначный тип базового класса соответственно;
  • […]

В вашей ситуации компилятор интерпретирует это как static_cast от Derived* в auto_ptr<Base>и в этом static_castуказатель на объект типа производного класса преобразуется в указатель однозначного типа базового класса. Так что, похоже, стандарт это позволяет.

Тем не менее, преобразование из Derived* в Base* подразумевается, это просто происходит как часть явное другое преобразование. Так что, в конце концов, стандарт действительно не позволяет этого.

Вы можете сообщить об этом как об ошибке. От CSQкомментируем, узнаем что есть связанный отчет, в котором явный static_cast также позволяет это преобразование, но это не совсем то же самое. В этом случае преобразование из Derived* в Base* явный, но здесь он неявный, и Visual C ++ обычно отклоняет его в неявных преобразованиях.

Обратите внимание, что в функциональных приведениях, которые используют несколько выражений, это неправильное толкование невозможно: компилятор правильно отклоняет следующее:

class Base { };
class Derived : Base { };

template <typename T>
class Ptr {
public:
Ptr(T *a, T *b) { }
};

int main() {
Ptr<Base>(new Derived, new Derived);
// error C2243: 'type cast' : conversion from 'Derived *' to 'Base *' exists, but is inaccessible
}
3

Другие решения


По вопросам рекламы [email protected]