Вот несколько идей, которые могут помочь, если вы не можете использовать решение PIMPL из другого ответа. Они основаны либо на CRTP, либо на тегах конструктора:
#include <type_traits>
struct A;
template<typename T>
struct Base final {
};
// Specialise Base for A to be non-final
template<>
struct Base<A> {};
struct A : Base<A> {};
Идея первого фрагмента состоит в том, чтобы иметь final
CRTP-базу и добавить неокончательную специализацию для класса A
. Очевидно, что это позволяет пользователям (или вам) при необходимости определять больше разрешенных производных, но предотвращает случайное наследование.
template<char...>
struct password {};
struct Base2 {
Base2() = delete;
template<char... pwd>
Base2(password<pwd...> ) {
using expected_pwd = password<'t','o','p','s','e','c','r','e','t'>;
static_assert(std::is_same_v<password<pwd...>, expected_pwd>);
}
};
struct A2 : Base2 {
A2() : Base2(password<'t','o','p','s','e','c','r','e','t'>{}) {}
};
struct A3 : Base2 {
// No way to construct Base2 without correct password :)
A3() : Base2(password<'b', 'a', 'm', 'm'>{}) {}
};
Идея этого второго фрагмента (который является скорее шуткой, чем реальным кодом ...) состоит в том, чтобы заменить конструктор по умолчанию класса Base2
структурой password
. Внутри реализации вы проверяете пароль, присвоенный конструктору. В реализации производного класса вы просто вызываете конструктор Base2
с правильным паролем. Конечно, ваши пользователи могут быть производными от базового класса, но поскольку пароль полностью скрыт в реализации, нет возможности создать производные объекты :)
int main(){
auto a = A{};
auto a2 = A2{};
auto a3 = A3{}; // fails
return 0;
}
person
florestan
schedule
07.01.2019