Любопытно повторяющийся шаблон - Наследование и друзья

0

вступление

Я пытаюсь использовать Curiously Recurring Template Pattern, чтобы сделать вещи доступными для хранения в группах hdf5. Но у меня две проблемы:

  1. Как сделать реализацию участника частной?
  2. Как использовать CRTP с пространствами имен?

Что я хочу

(Код следует) hdf5::Group представляет группу hdf5 и может хранить наборы данных. hdf5::Storable - это класс шаблонов CRTP. Функция hdf5::store должна взять группу и сохраняемый объект и вызвать реализацию объекта. Все это должно быть внутри пространства имен hdf5 чтобы сохранить чистоту.

A реализует хранилище. Он живет за пределами пространства имен hdf5 (например, глобального или другого пространства имен). Метод реализации A::store должен быть закрытым, чтобы убедиться, что каждый использует hdf5::store.

Что у меня есть

В этих частях есть замечания, указывающие на проблемы.

#include <string>
#include <iostream>

namespace hdf5 {

/// A group can contain other groups, and datasets.
class Group {
public:
    /// Create a group under a parent group.
    Group(Group* parent, const std::string &name)
        : parent_(parent), name_(name)
    {
        std::cout << "Creating group \"" << name << "\"";
        if (parent != nullptr)
            std::cout << " under \"" << parent->name_ << "\"";
        std::cout  << "." << std::endl;
    };

    /// Create a root group.
    Group() : Group(nullptr, "root") { }

    /// Create a dataset inside.
    void create_dataset(const std::string &name)
    {
        std::cout << "Creating dataset \"" << name << "\""
            << " under \"" << name_ << "\"." << std::endl;
    }

private:
    Group *parent_;
    std::string name_;
};

/** Abstraction of a storable class.
 *
 * Curiously recurring template pattern.
 * Makes it possible to write
 *
 *     store(grp, obj);
 *
 */
template<class Derived>
class Storable {
    friend void hdf5::store(hdf5::Group &grp, const Derived &obj) {
        obj.store(grp);
    }
};

}  // namespace hdft


/// Some data class that should be storable.
class A : private hdf5::Storable<A> {
public:
    A(const std::string &name) : name_(name) { }

/*
 * Why can't I make it private? 'store' should be friend.
 *
 *     test.cc: In instantiation of ‘void hdf5::store(hdf5::Group&, const A&):
 *     test.cc:104:19:   required from here
 *     test.cc:72:10: error: ‘void A::store(hdf5::Group&) const is private
 *          void store(hdf5::Group &grp) const {
 *               ^
 *     test.cc:45:9: error: within this context
 *              obj.store(grp);
 *              ^
 */
// private:
public:
    /// Implementation of the storage
    void store(hdf5::Group &grp) const {
        grp.create_dataset(name_);
    }

private:
    std::string name_;
};


/// Demonstration.
int main(void) {
    hdf5::Group root,
          grpa(&root, std::string("group_a")),
          grpb(&root, std::string("group_b"));
    A a1(std::string("A1")), a2(std::string("A2"));

    /*
     * This is what I want, but it doesn't compile:
     *
     *     test.cc: In function ‘int main():
     *     test.cc:96:5: error: ‘store is not a member of ‘hdf5
     *          hdf5::store(root, a1);
     *          ^
     */
    // hdf5::store(root, a1);
    // hdf5::store(root, a2);
    // hdf5::store(grpa, a1);
    // hdf5::store(grpb, a2);

    /*
     * This OTOH compiles and runs.
     */
    store(root, a1);
    store(root, a2);
    store(grpa, a1);
    store(grpb, a2);
}

Ожидаемый результат

Creating group "root".
Creating group "group_a" under "root".
Creating group "group_b" under "root".
Creating dataset "A1" under "root".
Creating dataset "A2" under "root".
Creating dataset "A1" under "group_a".
Creating dataset "A2" under "group_b".
  • 0
    дружба не переходная.
  • 2
    Что ж, это правда, что дружба не транзитивна, но в данном случае это проблема дружбы, которая не передается вашим потомкам. Насколько я понимаю, дружба работает на индивидуальной основе.
Показать ещё 1 комментарий
Теги:
c++11
namespaces
templates
crtp

1 ответ

1
Лучший ответ

Представляется, что следующие изменения: https://ideone.com/CRuLkb

namespace hdf5 {

// Previous stuff

template <class Derived> void store(hdf5::Group &grp, const Derived&obj);

template<class Derived>
class Storable {
    static void store(hdf5::Group &grp, const Derived&obj)
    {
        obj.store(grp);
    }

    friend void hdf5::store<>(hdf5::Group &grp, const Derived&obj);
};

template <class Derived>
void store(hdf5::Group &grp, const Derived&obj) {
    Storable<Derived>::store(grp, obj);
}


}  // namespace hdf5


/// Some data class that should be storable.
class A : private hdf5::Storable<A> {
    friend class hdf5::Storable<A>;
private:
    /// Implementation of the storage
    void store(hdf5::Group &grp) const {
        grp.create_dataset(name_);
    }
private:
    std::string name_;
};
  • 0
    Спасибо, это работает. Знаете ли вы, если есть способ написать это, чтобы вам не нужно объявление друга в A ? Кроме того, чтобы сделать A::store публичным, то есть. Как я понимаю ваше решение и то, что я читаю в другом месте, друзья не наследуются. Единственный способ, которым они могут быть эффективно унаследованы, это через виртуальные методы, верно?
  • 1
    Если вы создадите virtual hdf5::Storable<A>::store(hdf5::Group &grp) const , A::Store может быть hdf5::Storable<A> через hdf5::Storable<A> как static_cast<const hdf5::Storable<A>&>(obj).store(grp); и вы можете удалить friend .
Показать ещё 1 комментарий

Ещё вопросы

Сообщество Overcoder
Наверх
Меню