【问题标题】:Is there a explicit copy是否有明确的副本
【发布时间】:2011-05-16 04:43:29
【问题描述】:

我正在寻找这样的语法:

class Hugo
{
    Hugo();
    explicit Hugo( const Hugo& hugo );

    Hugo GetRandomHugo()
    {
        Hugo hugo;
        hugo.value = rand();
                                  // this would fail:
                                  //    return hugo;

        return Hugo(hugo);        // explicit copy!!
    }
};

换句话说:我正在寻找一种显式的复制语法,以允许方法返回一个副本,即使我的复制构造函数是显式的。

我使用的是 GCC 4.4.5。

非常感谢,

查理

【问题讨论】:

  • 原样的代码有什么问题?
  • 为什么你的复制构造函数是显式的?为什么说,“我希望我的复制构造函数是显式的,但我也想执行隐式复制”?使构造函数显式化的全部意义在于防止隐式转换,但复制构造函数并没有真正“转换”任何东西,它的存在是为了支持初始化、参数传递等。
  • @Steve:据我了解,他想要执行/允许隐式复制,同时仍然具有启用按值传递语义的显式语法。
  • 错误:没有匹配函数调用'Hugo::Hugo(Hugo)'
  • 看起来 C++ 非常擅长创造新类型的问题。

标签: c++ gcc explicit-constructor


【解决方案1】:

你不能:按值返回是一个隐式复制结构。在这里,返回尝试隐式复制您显式复制构造的临时文件。

从 8.5/12 开始:

初始化发生在 参数传递,函数返回, 抛出异常(15.1),处理 例外 (15.3),以及 大括号括起来的初始值设定项列表 (8.5.1) 称为复制初始化 并且等价于形式:

T x = a;

【讨论】:

  • 有这样的语法难道没有意义——告诉编译器:“是的,我想显式复制”
  • @Charly:我们有T x(a)(直接初始化,就是你写的)。从某种意义上说,这里的问题是return 语句“隐藏”了一个额外的初始化,它必然是复制初始化。
  • @Charly:为什么会这样?在任何情况下,都应该透明地处理副本,因为允许编译器忽略它们(但它仍然需要找到一个可访问的副本构造函数)。
  • @Charly:为什么这有意义?拥有复制构​​造函数并使其“特殊”的全部意义在于编译器可以在需要时调用它。当它需要复制一个对象时,它知道如何去做。如果你想要一个明确的语法,只需添加一个Copy() 函数。请注意,如果你没有有一个复制构造函数,你会破坏很多东西。您将无法按值返回,也无法将其存储在容器中。
【解决方案2】:

您可以通过明确的HugoCopy 类来解决此问题,如下所示

class HugoCopy;
class Hugo {
public:
    Hugo() { ... }  
    Hugo(HugoCopy const&);
    explicit Hugo(Hugo const&) { ... }
};

struct HugoCopy { 
    HugoCopy(Hugo const& hugo) 
      :hugo(hugo)
    { }

    Hugo const& hugo;
};

Hugo::Hugo(HugoCopy const&) { ... }

现在以下语义适用

Hugo a;
Hugo b = a; // forbidden
Hugo c(a); // allowed
Hugo d = HugoCopy(a); // allowed

Hugo f() {
  Hugo a;
  return a; // forbidden
  return HugoCopy(a); // allowed
}

或者,您可以使用转换函数

class Hugo {
public:
    Hugo() { ... }  
    explicit Hugo(Hugo const&) { ... }
};

struct HugoCopy { 
    HugoCopy(Hugo const& hugo) 
      :hugo(hugo)
    { }
    operator Hugo const&() { return hugo; }

private:
    Hugo const& hugo;
};

这依赖于 C++ 语言的一个微妙角落。所以如果你使用它,你最好知道你在做什么或者你不这样做:它首先调用 HugoCopy 上的转换函数(或者在第一种情况下,Hugo 的构造函数)得到一个Hugo /Hugo const&,然后它直接用Hugo 对象初始化目标Hugo 对象。 GCC 不喜欢该代码,但 Clang 和 Comeau/EDG 根据上述语义接受它。

【讨论】:

    【解决方案3】:
    return Hugo(hugo); 
    

    这只是在返回之前创建一个额外的副本。然后,实际的 return 语句获取该副本并再次复制它。复制构造函数的重点在于它可以隐式使用,只要我们或编译器需要复制一个对象。

    如果你想要一个明确的语法,你可以在类中添加一个Clone()Copy() 函数,但它不能替换复制构造函数。

    每次编译器需要复制一个对象时(例如,当通过值作为函数参数传递它时,或者从函数中返回它时),它需要创建一个对象的副本。您无法为编译器执行此操作,因为您看不到调用者和被调用者之间的“转换”代码。您可以将对象复制到被调用函数的内部 或外部,但是您无法从被调用者的主体复制到调用者。只有编译器才能做到这一点,为了做到这一点,它需要能够随意复制对象——这是通过复制构造函数完成的。

    【讨论】:

      猜你喜欢
      • 2012-05-31
      • 1970-01-01
      • 2020-09-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多