【问题标题】:Can I make error when diamond inheritance with template?使用模板继承钻石时会出错吗?
【发布时间】:2022-01-21 08:02:14
【问题描述】:

我想在继承重复时引发错误。这是我找到它的方法。

#include <utility>

class Person {};

class Man       : public Person {};
class Woman     : public Person {};

template <typename... Types>
class merge_class : public Types... {};

template <typename... Types>
struct condition
{
    using merge = merge_class<Types...>;
    
    using type = std::enable_if<
        std::is_convertible<merge, Person>::value // condition
        , merge>::type;
};

class BummooKim : public condition<Man>::type {};
class Daniel : public condition<Woman>::type {};
//class Unkown : public condition<Man, Woman>::type {}; // There is an error in the declaration.

但是,我发现如果有非默认构造函数,这种方式就不能用了。

我想知道是否有关键字表明它必须是单继承的。

如果 c++ 不支持“关键字”,我想要另一种方式。

示例

class OtherWay : public condition<Man, Other>::type 
{
    OtherWay() : Man() {}
};

【问题讨论】:

  • 不确定,但这似乎类似于继承中的钻石问题 - 人 -> 男人,女人 -> 未知。
  • 我发现自己遇到的问题是混杂的,不清楚的。
  • 我一头雾水,要不要解决钻石继承的这个错误?如果是,请阅读this。
  • 我想抛出错误而不是使用虚拟公共。
  • 我认为这种做法类似于在虚拟继承中创建一个虚拟类。我通过向虚拟类添加条件来实现它。

标签: c++ templates inheritance


【解决方案1】:

您遇到的问题称为菱形继承 (https://www.makeuseof.com/what-is-diamond-problem-in-cpp/),除了“接口”外,最好避免使用 IMO。我更喜欢使用组合实现,也称为 mixin 模式(它又使用 CRTP,奇怪的递归模板模式)。在这种模式中,您实现了不同的功能(例如,在以下示例中,男人可以喊叫,女人可以微笑,所有人都可以打招呼。)。

在编译时检测多重继承是不可能的(C++ 中可能启用它的功能从未通过标准委员会)。

我展示了一种至少在编译时检测到一个人不能同时是男人和女人的方法(至少在这个例子中)。

在 MSVC 中,此程序将给出以下编译错误:
错误 C2338 同时是男人和女人,在此程序中不正确

#include <iostream>
#include <string>

//-----------------------------------------------------------------------------
// multiple inheritance from interfaces (abstract base classes is fine)

// class to introduce a concept of an interface 
// and set all the constructors/destructors default behavior.

class Interface
{
public:
    virtual ~Interface() = default;

protected:
    Interface() = default;                      // protected constructor, avoids accidental instantiation
};

//-----------------------------------------------------------------------------
// for each aspect/capability of a person define a seperate interface
// this will also allow client code to cast an object to either of those
// interfaces to check if functionality is available

class PersonItf :
    public Interface
{
public:
    virtual void SayHi() const = 0;
};

//-----------------------------------------------------------------------------
// A man can shout

class ManItf :
    public Interface
{
public:
    virtual void Shout() const = 0;
};

//-----------------------------------------------------------------------------
// A woman can smile

class WomanItf :
    public Interface
{
public:
    virtual void Smile() const = 0;
};

//-----------------------------------------------------------------------------
// mixin classes for reusable code

template<typename base_t>
class PersonImpl :
    public PersonItf
{
public:
    void SayHi() const override
    {
        std::cout << "Hi!\n";
    }
};

template<typename base_t>
class ManImpl :
    public ManItf
{
public:
    void Shout() const override
    {
        std::cout << "Yohoohoooo!\n";
    };
};

template<typename base_t>
class WomanImpl:
    public WomanItf
{
public:
    void Smile() const override
    {
        std::cout << "Smile!\n";
    };
};


//-----------------------------------------------------------------------------
// now we can group capabilities together in classes
// 

class Man :
    public ManImpl<Man>
{
};

class Woman :
    public WomanImpl<Woman>
{
};

class ManAndWoman :
    public ManImpl<ManAndWoman>,
    public WomanImpl<ManAndWoman>
{
};

//-----------------------------------------------------------------------------
// this Person class will check validity of the composition
// at compile time.

template<typename type_t>
struct Person :
    public PersonImpl<type_t>,
    public type_t
{
    static_assert(!(std::is_base_of_v<WomanItf, type_t>&& std::is_base_of_v<ManItf, type_t>), "Being both a Man and a Woman is not correct in this program\n");
};

//-----------------------------------------------------------------------------

class Daniel : public Person<ManAndWoman> {};
class Santa : public Person<Man> {};

int main()
{
    Daniel daniel;
    Santa santa;

    daniel.SayHi();
    santa.Shout();

    return 0;
}

【讨论】:

  • 该错误消息 XD。只有逗号不需要,只有副词动名词后才需要逗号。这个作为名词(主语)
  • 删除了“,”...只是我的想法在那里稍作停顿以找到一个公式。希望对所有读者保持开放的心态。
【解决方案2】:

定义

#include <utility>

namespace Definer
{
    // define
    namespace Classification
    {
        class Person { unsigned int age; };
    }
    class Man       : public Classification::Person {};
    class Woman     : public Classification::Person {};

    // conditions
    template <typename Derived>
    static constexpr bool is_convertible_person()
    {
        constexpr bool result = std::is_convertible<Derived, Classification::Person>::value;
        static_assert(result, "Person is duplicated.");
        return result;
    }

    template <typename Derived>
    concept Condition
        = is_convertible_person<Derived>();
}

如何通过继承使之成为可能

namespace Definer
{
    template <typename Derived>
        requires Condition<Derived>
    class Requires
    {};
}

class Daniel :
    public Definer::Man,
    public Definer::Requires<Daniel> // Not require, but similar
{};

比较班级规模。并犯错误。

class Compare :
    public Definer::Man
{};
constexpr size_t size_of_Daniel = sizeof(Daniel);   // 4U
constexpr size_t size_of_Compare = sizeof(Compare); // 4U

// This class make an error well, except for repeated occurrences.
class Santa :
    public Definer::Man,
    public Definer::Woman,
    public Definer::Requires<Santa> // Not require, but similar
{};

//class Desire :
//  public Definer::Man
//  requires Definer::Condition<Desire> // It is not support now. I don't know if it will be possible later. 
                                        // 2021.12.20 c++20 preview
//{};

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-05-16
    • 1970-01-01
    • 2022-01-12
    • 2020-10-03
    • 1970-01-01
    • 2012-11-07
    • 1970-01-01
    相关资源
    最近更新 更多