Идиоматический способ предотвращения нарезки?
Иногда это может раздражать, что c++ по умолчанию разрешает нарезку. Например
struct foo { int a; };
struct bar : foo { int b; };
int main() {
bar x{1,2};
foo y = x; // <- I dont want this to compile!
}
Это компилируется и работает как ожидалось ! Хотя, что если я не хочу включать нарезку?
Каков идиоматический способ написать foo
, что нельзя нарезать экземпляры любого производного класса?
Ответы
Ответ 1
Я не уверен, существует ли для него именованная идиома, но вы можете добавить удаленную функцию к набору перегрузки, которая лучше соответствует операциям среза базовых классов. Если вы измените foo
на
struct foo
{
int a;
foo() = default; // you have to add this because of the template constructor
template<typename T>
foo(const T&) = delete; // error trying to copy anything but a foo
template<typename T>
foo& operator=(const T&) = delete; // error assigning anything else but a foo
};
тогда вы можете копировать только конструкции или назначать foo
для foo
. Любой другой тип выберет шаблон функции, и вы получите ошибку об использовании удаленной функции. Это означает, что ваш класс и использующие его классы больше не могут быть агрегатными. Поскольку добавляемые элементы являются шаблонами, они не рассматриваются как конструкторы копирования или операторы копирования, поэтому вы получите конструкторы копирования и перемещения по умолчанию и операторы назначения.
Ответ 2
С 2011 года идиоматическим способом стало использование auto
:
#include <iostream>
struct foo { int a; };
struct bar : foo { int b; };
int main() {
bar x{1,2};
auto y = x; // <- y is a bar
}
Если вы хотите активно предотвратить нарезку, есть несколько способов:
Обычно наиболее предпочтительный способ, если только вам не нужно наследование (часто это не так), это использовать инкапсуляцию:
#include <iostream>
struct foo { int a; };
struct bar
{
bar(int a, int b)
: foo_(a)
, b(b)
{}
int b;
int get_a() const { return foo_.a; }
private:
foo foo_;
};
int main() {
bar x{1,2};
// foo y = x; // <- does not compile
}
Другим более специализированным способом может быть изменение разрешений для операторов копирования:
#include <iostream>
struct foo {
int a;
protected:
foo(foo const&) = default;
foo(foo&&) = default;
foo& operator=(foo const&) = default;
foo& operator=(foo&&) = default;
};
struct bar : foo
{
bar(int a, int b)
: foo{a}, b{b}
{}
int b;
};
int main() {
auto x = bar (1,2);
// foo y = x; // <- does not compile
}
Ответ 3
Вы можете предотвратить копирование базы за пределы функций-членов производных классов и самой базы, объявив конструктор копирования защищенным:
struct foo {
// ...
protected:
foo(foo&) = default;
};