10 回答
您的复制构造函数中有两件值得怀疑的事情。
首先,您已经明确了复制构造函数(这是一个值得怀疑的事情),所以您(理论上)需要这样做:
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
对默认构造函数有什么影响。
您不想将这些构造函数中的任何一个标记为显式 - 编译器需要隐式使用它们,尤其是复制构造函数。你想通过明确标记它们来实现什么?
首先,默认构造函数和复制构造函数都不应该是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
};
尝试不显式?我觉得:
Foo foo = Foo()
创建一个隐式副本,因此不会触发显式复制构造函数。
编辑:
这只是答案的一半。请参阅 Charles Bailey 或 UncleBens 帖子了解为什么需要 const。
复制构造函数不应该是显式的(这使得它在此处和许多其他完全合理的上下文中不可调用,例如在按值传递或返回时)。
接下来它应该通过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();
此外,如果这些构造函数都不打算做任何事情,则根本不需要定义它们 - 编译器会自动提供它们(如果您定义任何其他构造函数,则不会自动生成默认构造函数)。
您的问题在于实例化。您不需要Foo d = Foo();
默认构造函数。
保持你的类不变,但试试这个实例化:
Foo d;
实际上,您甚至不需要Foo d = Foo(arguments);
使用参数进行构造。那应该是这样的:
Foo d(arguments);
Foo d = Foo();
应该
Foo d;
第一行创建一个 Foo 实例,然后将其复制到 d;
编译器告诉你......使用这个:
Foo(const Foo&) {}
您可以通过两种方式中的任何一种来解决问题。一个(Randolpho 已经建议)是消除使用复制 ctor。另一种是编写适当的复制ctor:
Foo (Foo const &) {}
您通常希望两者都做。
编辑:看着它,我的最后一条评论很容易被误解。相当多的类根本不需要复制 ctor,但如果您确实需要复制 ctor,它通常应该具有上述形式(不明确,并将 const 引用作为参数)。
class Foo
{
public:
explicit Foo() {}
explicit Foo(const Foo&) {}
};
Foo d = Foo()