【问题标题】:implicit conversion at call by value and polymorphism按值和多态调用的隐式转换
【发布时间】:2013-06-21 07:33:37
【问题描述】:

考虑以下场景: 一些多态类:

struct iClass 
{
  virtual ~iClass(){} 
};
struct CommonClass : public iClass 
{
  char str[128];
  CommonClass() { ... }
  ...
};
struct SpecificClass : public iClass
{
  char str[32];
  SpecificClass () { ... }
  SpecificClass (SpecificClass& src) { strcpy(...); ... }
  SpecificClass (CommonClass& src) { strcpy(...); ... }
  SpecificClass (const CommonClass& src) { strcpy(...); ... }
  void foo() { ... }
};

还有一个函数:

void someFunc(SpecificClass sc) { sc.foo(); } // pass by value: i want it copied!

int main ()
{
  CommonClass comCl;
  someFunc(comCl);  // <- Error: no matching function for call to 'SpecificClass::SpecificClass(SpecificClass)' NOTE: no &
  SpecificClass specCl(comCl);
  someFunc(specCl); // Works normal, but the str gets copied double times this way, isnt it?
  return 0;
}

为什么编译器不允许在第一个函数调用中从 CommonClass 转换为 SpecificClass,尽管无论如何都会在调用中构造一个新的 SpecificClass 并且有一个用于此特定转换的构造函数? 为什么我会收到这个奇怪的错误信息?在没有引用的情况下调用复制构造函数? 谁能分享一些对此问题的见解?

顺便说一句,我必须使用 gcc 4.1.2

【问题讨论】:

  • 您的构造函数采用CommonClass &amp;,但您需要一个接受const CommonClass &amp; 的构造函数。就目前而言,您正在尝试将右值绑定到非 const 左值引用。
  • 你的意思是什么部分是右值?我在这里没有看到任何常量?
  • 好吧,我在 commonClass 的 specificClass 中添加了一个 const 构造函数。还是同样的错误!但现在又多了一个“候选人”;)
  • 是的,在普通的复制构造函数中也需要使用const SpecificClass &amp;

标签: c++ gcc constructor polymorphism implicit-conversion


【解决方案1】:

someFunc 需要SpecificClass 类型的参数,而您提供CommonClass 类型之一。既然你已经定义了一个转换构造函数

SpecificClass::SpecificClass(CommonClass &)

这种转换可以隐式执行,到目前为止效果很好。

[不过,请注意,您的转换构造函数采用非 const 引用,这在这里有效,因为您实际上为它提供了一个左值 - 但是,一般来说,我假设将转换构造函数的参数作为 @ 987654325@ 通常更好(因为从一种类型到另一种类型的转换通常不会改变原始类型)。]

但是,问题出现在下一步:现在需要将新转换的SpecificClass 类型的对象复制到someFunc 的函数参数sc 中。为此,您需要一个 SpecificClass 的复制构造函数,它可以采用右值(这就是新转换的 SpecificClass 对象,因为它是一个临时对象)。

你的复制构造函数

SpecificClass::SpecificClass(SpecificClass &)

被声明为采用非常量左值引用,它不能绑定到临时值。所以你必须把它改成

SpecificClass::SpecificClass(const SpecificClass &)

解决问题。无论如何,这是声明复制构造函数的常用方法。


我不禁注意到另一件事:您有一个函数someFunc,它只能在非常特定类型的对象上调用。然而,您将其称为一般(基类?)类型。显然,这可以按照您描述的方式工作,但它在许多方面是违反直觉的,并且与面向对象编程的原则不一致。这也让我想知道“特定”对象实际上是如何从“普通”对象创建的,即转换构造函数实际上做了什么。直观地说,我假设作为输入给出的“通用”对象缺少创建“特定”对象的“特定”信息。

无论如何,您可能需要重新考虑类层次结构和/或someFunc 的用途。不过,这些都与您的问题中描述的问题没有直接关系。

【讨论】:

  • 精彩的解释。感谢您的洞察力!太糟糕了,原本打算只调用一个构造函数(而不是通过“也许优化”)!有没有办法通过在函数调用上使用构造函数来实现这一点?
  • 所有这些类只包含一个 c 字符串,每个我从某个串行通信设备获得。我查看、识别并将它们传递给 apropiate 回调函数 (someFunc),该函数期望存在其他特定 getter 和 setter 的特定类,因此可以操纵字符串中编码的值。
  • @PhilippMichalski 嗯。关于优化(即确保只执行一份副本,而不是一份用于转换,一份用于函数调用):我可能错了,但我看不出有任何方法可以保证这一点。 (不过我很确定 GCC,即使是 4.1,也会执行这种优化,至少使用 --O2--O3。它抱怨缺少复制构造函数的事实并不意味着它没有优化代码。它出于正式原因的投诉。)
  • 好吧,我的 GCC 的“优化级别”实际上是“--O0”,所以我不能指望它:-D 我不知道为什么,但它是我的 PLC 供应商提供的默认值我不能改变它。我有时应该问他们...我认为这是因为那是他们的第一个原型 PLC 可使用 c++ 编程,这也可以解释为什么它仅适用于 GCC 4.1.2(或 2.95.3)...
  • 好吧...我必须说,如果你追求速度,那就不好了。在这种情况下,避免这一副本可能是您遇到的最少的问题。但是,您可能想要检查是否调用了第二个复制构造函数或现在(例如,通过向其中添加打印语句)。如果是这样,并且您不希望这样做,恐怕您必须为要复制的每种数据类型制作一个单独的 someFunc 版本。不过,将其定义为模板可以帮助您节省一些输入。
猜你喜欢
  • 2017-03-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-03-12
  • 1970-01-01
  • 2014-11-15
相关资源
最近更新 更多