关于C++:奇怪的重复模板模式——继承和朋友

Curiously Recurring Template Pattern — Inheritance and friends

简介

我正在尝试使用奇怪的重复模板模式,以使HDF5组中的内容可存储。但我有两个问题:

  • 如何使成员实现私有化?
  • 如何将CRTP与命名空间一起使用?
  • 我想要什么

    (代码如下)hdf5::Group代表一个hdf5组,可以存储数据集,hdf5::Storable是一个crtp模板类。函数hdf5::store应该取一个组和一个可存储对象,并调用该对象的实现。所有这些都应该在名称空间hdf5中,以保持事物的清洁。

    A实现了一个可存储的。它位于hdf5名称空间(例如全局或其他名称空间)之外。实现方法A::store应该是私有的,以确保每个人都使用hdf5::store

    我所拥有的

    有问题的部分有指出问题的评论。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    #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);
    }

    预期产量

    1
    2
    3
    4
    5
    6
    7
    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".


    以下更改似乎有效:https://ideone.com/crulkb

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    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_;
    };