12
4

10 回答 10

13

您的复制构造函数中有两件值得怀疑的事情。

首先,您已经明确了复制构造函数(这是一个值得怀疑的事情),所以您(理论上)需要这样做:

Foo d( (Foo()) );

其次,您的复制构造函数采用引用而不是const引用,这意味着您不能将它与临时Foo.

就个人而言,我只是explicit从复制构造函数中删除,并const尽可能将其作为参考。

请注意,explicit默认构造函数上的 没有效果。[*]explicit仅对可以使用单个参数调用的构造函数有影响。它可以防止它们被用于隐式转换。对于只接受零个或两个或多个参数的构造函数,它不起作用。

[注意:两者之间可能存在差异:

Foo d;

Foo d = Foo();

但在这种情况下,您有一个用户声明的默认构造函数,因此这不适用。]

编辑: [*] 我刚刚仔细检查了这一点,12.3.1 [class.conv.ctor] 说你可以创建一个默认构造函数explicit。在这种情况下,构造函数将用于执行默认初始化值初始化。老实说,我不理解 this 的价值,就好像你有一个用户声明的构造函数,然后它是一个非 POD 类型,即使非 POD 类型的本地对象如果没有初始化器,它们也会被默认初始化该子句所说的可以由explicit默认构造函数完成。也许有人可以指出它确实有所作为的极端情况,但现在我看不出explicit对默认构造函数有什么影响。

于 2010-05-05T19:55:19.977 回答
5

您不想将这些构造函数中的任何一个标记为显式 - 编译器需要隐式使用它们,尤其是复制构造函数。你想通过明确标记它们来实现什么?

于 2010-05-05T19:56:29.790 回答
4

首先,默认构造函数和复制构造函数都不应该是explicit. explicit如果构造函数采用其他类型的单个参数,则只需要创建一个构造函数,以防止从该类型进行隐式转换。复制构造函数引用类本身,因此不存在不必要的转换的危险。

其次,确保复制构造函数有const引用。

第三,Foo f;是拥有类 foo 的默认构造对象的正确方法。请注意这Foo f();是错误的,因为编译器会将其解释为f()返回 class 对象的函数声明Foo

第四,如果你已经编写了自己的复制构造函数,那么你也应该编写赋值运算符。


class Foo
{
  Foo() {} // no need to make explicit.  Nothing to convert from.

  Foo(const &Foo f) {} // again, nothing wrong with conversion from Foo to Foo

  explicit Foo(int a) {}  // need explicit to prevent accidental passing of an int
                          // to a function that takes Foo as an argument
};

于 2010-05-05T20:10:21.677 回答
3

尝试不显式?我觉得:

Foo foo = Foo()

创建一个隐式副本,因此不会触发显式复制构造函数。

编辑:

这只是答案的一半。请参阅 Charles Bailey 或 UncleBens 帖子了解为什么需要 const。

于 2010-05-05T19:55:25.100 回答
3

复制构造函数不应该是显式的(这使得它在此处和许多其他完全合理的上下文中不可调用,例如在按值传递或返回时)。

接下来它应该通过const引用获取参数,否则它不能绑定到临时对象。

Foo f = Foo();
        ^^^^^
          |
          --- this is a temporary that cannot be passed to a function
              that accepts a non-const reference

此外,没有理由使默认构造函数显式:此关键字仅对可以使用仅一个参数调用的构造函数(复制构造函数除外)有意义,在这种情况下,它可以防止通过该参数将其他类型隐式转换为 Foo构造函数。例如,如果采用int的构造函数是显式的,则此类情况将无法编译:

Foo f;
f = 1;  //assuming no operator= overload for (types convertible from) int
        //this implicitly performs f = Foo(1);

Foo g = 10;

void x(Foo);
x(20);

总而言之:

class Foo
{
public:
    Foo();
    Foo(const Foo&);
    //...
};

Foo x = Foo();

此外,如果这些构造函数都不打算做任何事情,则根本不需要定义它们 - 编译器会自动提供它们(如果您定义任何其他构造函数,则不会自动生成默认构造函数)。

于 2010-05-05T20:04:30.353 回答
2

您的问题在于实例化。您不需要Foo d = Foo();默认构造函数。

保持你的类不变,但试试这个实例化:

Foo d;

实际上,您甚至不需要Foo d = Foo(arguments);使用参数进行构造。那应该是这样的:

Foo d(arguments);
于 2010-05-05T19:54:37.887 回答
2
Foo d = Foo();

应该

Foo d;

第一行创建一个 Foo 实例,然后将其复制到 d;

于 2010-05-05T19:55:17.310 回答
1

编译器告诉你......使用这个:

Foo(const Foo&) {}
于 2010-05-05T19:56:04.643 回答
0

您可以通过两种方式中的任何一种来解决问题。一个(Randolpho 已经建议)是消除使用复制 ctor。另一种是编写适当的复制ctor:

Foo (Foo const &) {}

您通常希望两者都做。

编辑:看着它,我的最后一条评论很容易被误解。相当多的类根本不需要复制 ctor,但如果您确实需要复制 ctor,它通常应该具有上述形式(不明确,并将 const 引用作为参数)。

于 2010-05-05T19:57:06.423 回答
-1
class Foo
{
public:
    explicit Foo() {}
    explicit Foo(const Foo&) {}
};

Foo d = Foo()
于 2010-05-05T19:59:25.823 回答