Справочная информация: это было обнаружено в 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) я пропустил, или это ошибка?
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
}