Следующий упрощенный (но все же компилируемый) пример иллюстрирует возможный сценарий назначения срезов.
#include <string>
struct Base
{
// Mutating method. Not a chance of making it virtual.
template <typename Anything>
Base& operator=(const Anything& x)
{
m_int = x.AsInteger();
return *this;
}
int AsInteger() const
{
return m_int;
}
int m_int;
};
struct Derived : public Base
{
template <typename Anything>
Derived& operator=(const Anything& x)
{
m_text = x.AsString();
Base::operator=(x);
return *this;
}
const std::string& AsString() const
{
return m_text;
}
// Invariant: Derived::m_text matches Base::m_x.
std::string m_text;
};
void ExamineBase(const Base* b)
{
b->AsInteger();
}
void ExamineBase(const Base& b)
{
b.AsInteger();
}
void InitBase(Base* b)
{
*b = Base();
}
void InitBase(Base& b)
{
b = Base();
}
int main()
{
Base b;
InitBase(b); // <----- (1)
InitBase(&b); // <----- (2)
Derived d;
Derived& ref = d;
Derived* ptr = &d;
ExamineBase(ref); // <----- (3)
ExamineBase(ptr); // <----- (4)
InitBase(ref); // <----- (5)
InitBase(ptr); // <----- (6)
return 0;
}
Строки (1), (2), (3) и (4) хороши.
Строки (5) и (6) демонстрируют проблему: они изменяют только базовый подобъект внутри законченного объекта, очевидно нарушая согласованность между Base :: m_int и Derived :: m_text.
I'mзаинтересованы в предотвращении такой модификации среза, сохраняя при этом достоверность строк (1), (2), (3) и (4).
Итак, вопросы:
aЕсть ли какие-нибудь приемы, которые могут помешать вызову неконстантных функций-членов базового класса через указатель на производный класс?
b) Существуют ли приемы, которые могут блокировать стандартное неявное преобразование из Derived*
в Base*
, но все же разрешить преобразование из Derived*
в const Base*
?