【问题标题】:Inheritance, Copy Constructors and Implicit Typecasting继承、复制构造函数和隐式类型转换
【发布时间】:2019-05-14 20:49:18
【问题描述】:

我有一个派生类,我希望能够使用复制构造函数来构造它,其中参数是基类的实例。

我相信这在 C++ 中应该是可能的。这是一个例子:

#include <string>


class Base
{

public:

    friend
    void swap(Base& l, Base& r)
    {
        using std::swap;

        swap(l.a, r.a);
    }

    Base()
        : a{1}
    {
    }

    Base(const int a)
        : a{a}
    {
    }

    virtual
    ~Base()
    {
    }

    Base(const Base& base)
        : a{base.a}
    {
    }

    Base(Base&& base)
        : Base()
    {
        swap(*this, base);
    }

    Base& operator=(Base base)
    {
        swap(*this, base);

        return *this;
    }

protected:

    int a;

};


class Derived : public Base
{


protected:

    std::string b;

};

int main()
{

    Base base(2);
    Derived derived(base);

}

错误(g++ main.cpp)是:

main.cpp: In function ‘int main()’:
main.cpp:71:31: error: no matching function for call to ‘Derived::Derived(Base&)’
     class Derived derived(base);
                               ^
main.cpp:57:7: note: candidate: Derived::Derived()
 class Derived : public Base
       ^~~~~~~
main.cpp:57:7: note:   candidate expects 0 arguments, 1 provided
main.cpp:57:7: note: candidate: Derived::Derived(const Derived&)
main.cpp:57:7: note:   no known conversion for argument 1 from ‘Base’ to ‘const Derived&’
main.cpp:57:7: note: candidate: Derived::Derived(Derived&&)
main.cpp:57:7: note:   no known conversion for argument 1 from ‘Base’ to ‘Derived&&’

因此编译器不知道如何将Base 的实例隐式转换为Derived

我认为这在 C++ 中应该是合法的。我需要明确的转换语句吗?

【问题讨论】:

  • 为什么这在任何语言中都应该是合法的?这与 Liskov 替换原则相反。
  • 这是错误的:`class Base base(2);派生类派生(基); ` 只需使用:Base base(2); Derived derived(base);
  • 这是一个链接。可能重复? stackoverflow.com/questions/347358/inheriting-constructors
  • 没有隐式类型转换这样的东西。根据定义,强制转换是一种显式类型转换。
  • 另请注意,没有单个虚函数的公共继承几乎总是一个错误,复制使用公共继承的对象也几乎总是一个错误。

标签: c++ inheritance casting copy-constructor


【解决方案1】:

我找到了我要找的东西(我不记得名字了,所以以前不能用谷歌搜索)。然而,由于我使用的是继承,这种技术实际上不起作用。 (或者至少我不知道如何让它工作。)

类型转换运算符:

Base::operator Derived()
{
    Derived derived;
    derived.a = a;
    return derived;
}

这实际上并没有编译,因为编译器不知道Derived 是什么。 (由于Derived继承自Base。)我不知道是否可以通过分离编译单元来完成这项工作。

【讨论】:

    【解决方案2】:

    你在做什么本身并没有多大意义,因为Base 不是Derived 的子类型,所以它不能用作它的替换/替代,但是你可以尝试让它有意义(与任何其他类型的初始化相同)通过编写转换构造函数:

    class Derived : public Base
    {
    public:
    Derived(const Base &bs) : Base(bs), b("constructed from base") {}
    
    protected:
    
        std::string b;
    
    };
    

    这将首先从bs 初始化Derived Base 部分,然后用一些值初始化字符串b(尽管如果您希望将其默认初始化为空字符串,则可以将其省略)。

    https://godbolt.org/z/GMELW_

    【讨论】:

    • 这看起来像我所追求的那种东西。这将起作用,但只是出于兴趣,有什么方法可以编写一个外部函数(非成员)来进行这种转换。以某种方式这样做会很好,这样我就不必编写许多版本的构造函数,而只需编写一个转换函数。我可能梦想过这一点,但在提出这个问题之前,我相当确定 C++ 有一种编写隐式或显式转换函数的方法——现在我不太确定。
    • @user3728501 你可以编写一个函数Derived fromBase(const Base &amp;bs),它的作用基本相同(但它需要声明为friend),但它对构造函数没有多大意义。同样,您仍然没有解释您真正想要什么以及为什么。为什么你认为你“必须编写许多版本的构造函数,而不是只编写一个转换函数”?正如我所展示的,您必须运行一个构造函数。您完全需要它的事实已经表明您可能做错了什么。从你其他的cmets来看,也许构图是关键
    【解决方案3】:

    有一个简单的方法可以解决这个问题:将Base 构造函数拉入Derived 的范围内。

    这可以通过using 语句来完成:

    class Derived : public Base
    {
    public:
        using Base::Base;  // Pulls in the Base class constructors into the scope of Derived
    
        ...
    };
    

    【讨论】:

    • 奇怪 - 我收到另一个错误“错误:使用 Base 的‘Base’之前预期的嵌套名称说明符;”
    • 上面应该是using Base::Base;
    • @user3728501 约瑟夫说了什么。
    • @JosephFranciscus 仍然不起作用 - 这是哪个 C++ 标准?
    • C++11 虽然你不能继承复制构造函数。 (这就是它失败的原因)stackoverflow.com/questions/28450803/…
    【解决方案4】:

    此语句Derived derived(base); 或简化B b(A()); 执行类型A 到类型B 的隐式转换,仅当class B 直接或间接继承自Class A 时才合法。

    为什么?因为class B 可能包含新信息,在您的情况下为string b,并且演员表不会“附加”信息。

    【讨论】:

      【解决方案5】:

      是的,您需要从Base 显式转换为Derived。每辆梅赛德斯都是汽车,但并非每辆汽车都是梅赛德斯。

      【讨论】:

      • 我该怎么做?
      • @Dan 你是对的!但是编译器会接受它。 ;) 我希望这会使第一个问题更容易理解。
      • @user3728501 关于什么?你有什么问题?你想达到什么目的?为什么要初始化 Derived from Base?一般来说,只有相反的工作(因为每个 Derived 都是 Base,而不是相反)。例如,Base 没有 std::string b 字段,那么它如何被初始化?如果您真的需要,您可以编写自定义转换构造函数 (Derived(const Base &amp;b) {...}),但这看起来确实是一个 XY 问题。
      • @DanM。我想让我的代码编译 - 如有必要,使用显式转换。目前我不知道最好的方法是什么,并且在典型的 stackoverflow 样式中,现在至少有 3 个 cmets/“answers”告诉我“我错了,我的代码无法编译”。是的,我知道 - 它甚至在问题中说存在编译错误。
      • @user3728501 因为您尝试做的事情没有意义(除非您解释原因),而且看起来您确实误解了继承的工作原理。同样,有一种方法可以“强制”它进行编译,但这可能只会导致运行时崩溃/SIGSEGV。我会用我现在能想到的唯一合理的解决方案来创建一个答案,但如果你不解释你期望得到什么,这似乎仍然毫无意义。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-09-03
      • 1970-01-01
      • 1970-01-01
      • 2013-03-01
      • 2012-05-26
      相关资源
      最近更新 更多