【问题标题】:Use constructor inheritance to provide conversion between sibling types使用构造函数继承提供兄弟类型之间的转换
【发布时间】:2015-05-14 21:57:38
【问题描述】:

我想允许以下行为:

struct SignalBase {};

struct Signal1 : public SignalBase
{ using SignalBase::SignalBase; };

struct Signal2 : public SignalBase
{ using SignalBase::SignalBase; };

int main()
{ 
    Signal1 s1;
    Signal2 s2(s1);
}

当然,这是行不通的,因为SignalBase 没有接收派生类型的构造函数,并且复制构造函数不是通过using 子句继承的。

我尝试使用 SignalBase 中的模板构造函数来解决这个问题:

// External function:
template<typename Derived, typename Base>
constexpr bool isDerived()
{
   return std::is_base_of<Base, Derived>::value and
      !std::is_same<Base, Derived>::value;
}

struct SignalBase
{
   template<typename Signal>
   SignalBase(Signal const& s)  // (1)
   { static_assert(isDerived<Signal, SignalBase>(), "Invalid copying"); }
};

由于构造函数 (1) 不能与复制构造函数混淆(如果我没记错的话,模板构造函数不被视为复制构造函数,因为非模板复制构造函数如果具有相同的签名,则优先于模板构造函数实例化后的复制构造函数)。

所以,构造函数(1)应该在派生类中继承:

// struct Signal1 inherits:
template<typename Signal>
Signal1(Signal const& s)  // (2)
{ static_assert(isDerived<Signal, SignalBase>(), "Invalid copying"); }

// struct Signal2 inherits:
template<typename Signal>
Signal2(Signal const& s)  // (3)
{ static_assert(isDerived<Signal, SignalBase>(), "Invalid copying"); }

还有这样一行:

Signal2 s2(s1);

应使用Signal = Signal1 实例化 (3),这是 Signal 的派生类,不会因 static_assert 子句而导致错误。

但这不会发生,因为编译错误表明没有找到从 Signal1Signal2 的转换。

为什么基构造函数没有被正确继承,派生类中真正继承了什么?

注意:在我的真实代码中(为简单起见,此示例中未显示),派生信号是模板,并且强制转换运算符仅适用于显式强制转换(使用 (Type)object 运算符),它在我在函数之间发送对象的地方使行太长。这就是为什么我想通过构造函数来寻找一种方法。

注意 2:请关注 C++ 问题(真的被继承了?),而不是其他 OOP 考虑因素(例如,从基类构造派生对象是否有意义?) ,因为这是我真实代码的一个非常简化的示例。

【问题讨论】:

  • Signal1Signal2SignalBase 作为它们最低的共同祖先。因此,在执行 Signal2 s2(s1) 时,保留除 s1SignalBase 部分之外的任何内容都是没有意义的。转换为SignalBase 应该这样做
  • 您希望Signal2 s2(s1) 做什么,确切地说?在一个明确的 OOP 类层次结构中,Signal2 根据定义对Signal1 一无所知。给定它不应该处理的东西的参数,它的构造函数应该如何创建一个有用的实例?这就像从Cat 构造Dog,因为两者都是Animals。我想知道继承是否适合这项工作。
  • @Christian Hackl 关键是派生类没有自己的数据成员。我创建它们只是为了消除其他模板和函数模板的歧义,但在某些地方我想重用与不同信号完美配合的函数。
  • @Rerito 看到我在帖子末尾的注释。
  • 为什么需要模板?请描述您在使用构造函数时遇到的具体问题,而不是 const SignalBase&amp;。哦,我明白了,你正试图让 using 工作。

标签: c++ templates c++11 constructor


【解决方案1】:

这可以在没有模板的情况下通过添加第二层基类来完成:

class SignalBase
{
    /* all the members */
};

struct CopyableSignalBase : SignalBase
{
    // not actually a copy constructor, so using will pick it up
    CopyableSignalBase(const SignalBase& src) : SignalBase(src) {}

    // make all other (not copy and not default) constructors available too
    using SignalBase::SignalBase;

    // since using doesn't introduce default constructor, provide it
    // if and only if SignalBase had one
    CopyableSignalBase() = default;
};

struct Signal1 : public CopyableSignalBase
{ using CopyableSignalBase::CopyableSignalBase; };

struct Signal2 : public CopyableSignalBase
{ using CopyableSignalBase::CopyableSignalBase; };

请注意,根据this feature matrix,在 VS14 之前,Visual Studio 根本无法处理继承构造函数。但是如果没有构造函数继承,整个问题就毫无意义。

【讨论】:

  • 如果你真的尝试Signal1 s1; Signal2 s2(s1);,这不会编译。
  • (即使你为Signal1添加了默认构造函数)
  • @ChristianHackl,您还需要其他默认构造函数,然后它会编译。
  • @ChristianHackl:我不知道从头开始构造对象的参数应该是什么。向CopyableSignalBase 添加一个简单的默认构造函数对我有用:rextester.com/KTPVR25461
  • @ChristianHackl:我认为 VC++ 还没有实现构造函数继承,请参阅blogs.msdn.com/b/vcblog/archive/2014/08/21/…CopyableSignalBase s2 = s1; 有效,Signal2 s2 = s1; 无效。下个版本应该没问题。
猜你喜欢
  • 2015-08-28
  • 1970-01-01
  • 2019-05-14
  • 2013-01-25
  • 1970-01-01
  • 1970-01-01
  • 2014-10-30
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多