Возможно ли обеспечить константность передаваемого объекта зависимости в C++

Вот что я хотел бы предотвратить:

class Dep; //for "dependency"

class C {
  private: 
    const Dep& dep_; //or std::shared_ptr if I want to guarantee proper lifetime of dependency
  public:
    C(const Dep& dep) : dep_(dep){}
}

Dep d(Arg arg);
C obj(d); //obj can't change d
d.some_non_const_method(); //But it's creator can :(

То, что я хотел бы получить, - то, что единственный правильный способ создать C объект был такой:

Dep d(Arg arg);
d.some_non_const_method();
const Dep d1(d); //or move constructor if appropriate
C obj(d1); //Object d1 must be const!
d1.some_non_const_method(); //compile error, so obj can be sure that the dependency he's got is really const.

Есть ли способ обеспечить это через синтаксис?

PS Зависимость предназначена для совместного использования, поэтому я не могу использовать std::unique_ptr Вот.

3 ответа

Решение

Я не думаю, что есть способ обеспечить это напрямую через синтаксис.

Проблема в том, что любой Dep& может быть преобразован свободно в const Dep&, Как вы правильно заметили с собой const Dep &d не означает, что это константа, скорее это обещание, что ваша функция никогда не изменит d,

В то время как другие ответы изобретательно используют методы перегрузки и удаления для создания ошибки всякий раз, когда неконстантный Dep передается напрямую, эти методы завершаются ошибкой, когда const Dep & передается из другого места.

// this could be arbitrarily complex and nested
std::unique_ptr<C> makeC(const Dep &d) {
     return std::make_unique<C>(d);
} 

void test() {
    Dep nonConstDep;
    auto pC = makeC(nonConstD); // conversion to const & is legal
    nonConstDep.mutate(); // Nooo!
}

Вам нужен неизменяемый класс dep, который не допускает мутации и преобразования.

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

Создать ImmutableDep обертка, которая имеет частный const Dep, Тогда вы можете ограничить свой класс, чтобы принимать только ссылки ImmutableDep.

class ImmutableDep {
     const Dep dep_;

public:
     inline const Dep &get() const { return dep_; }
};

class C {
     const Dep &dep_;
public:
     // we know that an ImmutableDep's dep really is immutable
     C(const ImmutableDep &dep) : dep_(dep.get()) {}
}

Таким образом, вы можете передавать константные или неконстантные ссылки на ImmutableDep сколько вам захочется, вам всегда гарантируется, что приват завернут Dep неизменен.

Если вам нужно изменить Dep только во время ограниченной фазы "строительства" я предлагаю использовать шаблон Builder и создать неизменный объект после того, как вы закончите, возможно, переместив что-либо большое в неизменяемый объект.

Если я правильно понимаю ваш вопрос, вы хотите только принять const аргумент, и хотите получить ошибку, если неconst аргумент пройден.

Простейшим способом должна быть маркировкаconst версия как delete:

class C {
  private:
    const Dep& dep_;
  public:
    C(const Dep& dep) : dep_(dep){}
    C(Dep &) = delete;
};

Затем:

Dep d;
C obj(d); // Error, deleted function selected

const Dep d1(d);
C obj(d1); // ok

Нечто в форме

class C
{
    C() = default;
    friend class D;
};

class D
{
    public:
    C create_C() = delete;
    C create_C() const {return C};
};

сделал бы это. Я сделал строительство Cprivate, D может получить к нему доступ, так как это friend из C, Затем на-const перегрузка create_C удален

Другие вопросы по тегам