【问题标题】:Foo f = Foo(); // no matching function for call to 'Foo::Foo(Foo)' ... huh?Foo f = Foo(); // 没有匹配的函数调用 'Foo::Foo(Foo)' ... 嗯?
【发布时间】:2010-05-06 22:20:14
【问题描述】:
class Foo
{
public:
    explicit Foo() {}
    explicit Foo(Foo&) {}
};

Foo d = Foo();

错误:没有匹配的函数调用 'Foo::Foo(Foo)'

我尝试按照错误提示将Foo(Foo&) 更改为Foo(Foo),AFAIK 不是有效的构造函数,果然我得到了:

错误:无效的构造函数;你的意思可能是“Foo (const Foo&)”

什么给了?我该如何解决这个问题? (顺便说一下,这是在 GCC 上)

【问题讨论】:

  • 编译器已经回答了你的问题...Foo (const Foo&)Foo d = Foo(); 正在调用复制构造函数。
  • +1 因为似乎没有人知道答案
  • +1 仅用于 url... foofoofoo-huh :D
  • Brian Roach 是对的,编译器回答了你的问题!您不能让复制构造函数获取Foo 的副本,因为它必须获取一个副本才能进入复制构造函数,这将无限递归......所以它建议了正确的签名。

标签: c++ gcc constructor explicit


【解决方案1】:

您的复制构造函数中有两个值得怀疑的地方。

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

Foo d( (Foo()) );

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

就我个人而言,我会从复制构造函数中删除 explicit,并尽可能将其设为 const 引用。

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

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

Foo d;

Foo d = Foo();

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

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

【讨论】:

  • 为什么要多出一组括号?
  • @clahey:绕过最棘手的解析。
  • 既然詹姆斯给出了正确的答案,我只能说:因为我是一个壁橱里的 lisp 粉丝。
  • 对于所有我不知道答案的问题,我都会做同样的事情:为看起来“最正确”的问题投票。恭喜查理。
  • @clahey:在模板和模板参数Foo 中,构造Foo d = Foo() 很有用,因为如果Foo 是原始类型,那么d 保持未初始化如果Foo d被使用了。相关:stackoverflow.com/questions/2143022/…
【解决方案2】:

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

【讨论】:

  • 回答您的问题:我想消除由我不想发生的转换引起的任何意外(...通过让编译器引发错误)。
  • @Kyle 但是您确实希望编译器能够进行复制 - 这不是转换。默认构造也不是。
  • 对,但是如果我尝试做Foo f = Bar(),我希望它爆炸,其中Bar 很高兴通过提供operator Foo() 允许自己转换为Foo(我想我有正确的思考过程..)
  • @Kyle 我认为不是。复制构造函数不执行转换 - 如果您想阻止该代码,您需要阻止 Bar 可转换为 Foo,而不是禁止复制 Foo。还有一个问题是为什么您首先要阻止转换 - 它们通常是一件好事,并为 C++ 提供了很多功能和可用性。
【解决方案3】:

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

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

第三,Foo f; 是拥有类 foo 的默认构造对象的正确方法。请注意Foo f(); 是错误的,因为编译器会将其解释为函数f() 的声明,它返回类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 };

【讨论】:

    【解决方案4】:

    尝试不显式?我认为:

    Foo foo = Foo()
    

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

    编辑:

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

    【讨论】:

    • 这是正确答案。此外,没有理由一开始就明确复制构造函数。显式构造函数旨在防止您意外地隐式转换类型。
    【解决方案5】:

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

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

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

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

    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 f; Foo g(f);)。
    • @James:谢谢,添加了说明。
    • +1 对于第二句和以下示例。正是 OP 出现问题的原因。
    【解决方案6】:
    Foo d = Foo();
    

    应该是

    Foo d;
    

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

    【讨论】:

    • 正确,但不是问题的答案——Foo d = Foo() 只是生成错误的简化。我本可以在其他地方拥有Foo d;Foo k = d;
    【解决方案7】:

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

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

    Foo d;
    

    事实上,你甚至不需要Foo d = Foo(arguments); 来构造参数。应该是这样的:

    Foo d(arguments);
    

    【讨论】:

    • Foo d = Foo() 只是生成错误的简化。我本可以在其他地方拥有Foo d;Foo k = d;
    • @Kyle:从您现在离开的许多 cmets 中,我可以看出您故意尝试调用复制构造函数。我希望你在你的问题中这么明确地说。我在您的问题中找不到任何暗示您甚至知道复制构造函数 is 的地方;您刚刚发布了代码并说“为什么它坏了?”我假设(是的,是的)你只是做错了。 Foo d = Foo(); 当然会在那个转瞬即逝的Foo 实例上调用复制构造函数,但这是你不应该真正的事情。
    • 正如其标题所暗示的那样,我的问题是关于奇怪的错误消息,这似乎不是给定代码的明显结果。我还没有掌握如此精确地绕过问题的技巧,以至于没有人可以回答错误的问题。请编辑您的答案,以便我为您投票。
    • @Kyle: 嘿... C++ 编译器错误通常趋于怪异。关于您的问题,我想说明确说明您的意图是一件好事(tm)。在这种情况下...假设您的意图是调用复制构造函数,但是编译器说您没有,而您显然有,所以WTF?关于我所做的假设,不要怪自己,怪我。问题是这么多人发布垃圾问题,我们往往不得不尝试找到“真正的问题”。我应该更加勤奋。
    【解决方案8】:

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

    Foo(const Foo&) {}
    

    【讨论】:

    • 反对票。从字面上看,签名应该是什么样子。 (不是它应该做的 - 什么都没有。)
    【解决方案9】:

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

    Foo (Foo const &) {}
    

    你通常都想同时做。

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

    【讨论】:

    • Foo(Foo&) 是一个正确的复制构造函数。对于他正在尝试做的事情,这根本不是正确的复制构造函数。
    • @Dennis:如果你喜欢,那很好——但是打开修改被复制对象之门的复制构造函数并不是我所说的“正确”。
    【解决方案10】:
    class Foo
    {
    public:
        explicit Foo() {}
        explicit Foo(const Foo&) {}
    };
    
    Foo d = Foo()
    

    【讨论】:

    • 不是我,但我可以告诉你,它仍然无法编译。
    猜你喜欢
    • 2010-12-12
    • 2014-11-09
    • 2023-03-17
    • 1970-01-01
    • 2017-06-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-01-30
    相关资源
    最近更新 更多