【问题标题】:C++ declares a function instead of calling a complex constructorC++ 声明一个函数而不是调用一个复杂的构造函数
【发布时间】:2012-03-19 14:38:21
【问题描述】:

首先,我知道 stackoverflow 上已经有类似的问题(thisthisthis one),这就是为什么我理解我的问题的原因。不幸的是,这并不能帮助我解决它。

虽然上述问题都与默认的无参数构造函数有关,但我在使用具有默认值的 双参数构造函数时遇到了问题 - 我试图构造一个仅调用构造函数的对象给定的第一个值,它被解析为函数声明而不是对象。

这是我的代码的一些 sn-ps(我重命名了类名,因为它们很长且不相关):

class algoContainer{
public:
algoContainer(algo1Virtual &alg1 = algo1Concrete::emptyInstance(),
      algo2Virtual &alg2 = algo2Concrete::instance());

someUsefulFunction();
};

class algo1Concrete : public algo1Virtual{
    private:
    algo1Concrete();
    public:
    static algo1Concrete &emptyInstance(); // "empty" instance treated
                                           // specifically
                                           //  -- uses private no arg constructor
    algo1Concrete(const std::vector<data> &myData); // construcotr
};

class algo1Virtual{
    // ... all functions virtual, no implementations ...
};


// ... similar for algo2Virtual/Concrete ...

Concrete 类中的所有函数都实现了,而Virtual 类中的所有函数都没有实现(构造函数和析构函数除外)。

所以,我现在的问题是我想做一些类似的事情:

std::vector <data> workData;
// fill workData
algoContainer myAC(algo1Concrete(workData));
myAC.someUsefulFunction(); // this line gives compile error

漂亮,可爱,优雅,但它不起作用(错误与我linked的所有问题相同)。我发现这个forum-tutorial 确实将该问题称为最令人烦恼的解析,但它的解决方案(在参数周围加上括号)并不能解决问题(它是一长串错误消息这种情况,但如果有帮助,我可以稍后在问题中对其进行编辑 - 这些都与继承虚函数有关)。

如果我使用带有默认所有参数的构造函数,我已经测试了我的代码,即使我只是单独构造第一个参数:

std::vector <data> workData;
// fill workData
algo1Concrete myA1(workData);
algoContainer myAC(myA1);

myAC.someUsefulFunction(); // now it works fine

algoContainer myAC2;
myAC2.someUsefulFunction(); // this also works

我可以按原样使用代码,但如果有人能给我一个更优雅的解决方案来解决我现在正在使用的问题,我将不胜感激。


EDIT:当我修复最令人头疼的解析时收到的错误消息

如果我使用带括号的代码:

algoContainer myAC((algo1Concrete(workData)));

我的错误是:

/some_path/main.cpp:47:65: error: no matching function for call to ‘algoContainer::algoContainer(algo1Concrete)’
/some_path/main.cpp:47:65: note: candidates are:
/some_path/algo/algocont.h:45:5: note: algoContainer::algoContainer(algo1Virtual&, algo2Virtual&)
/some_path/algo/algocont.h:45:5: note:   no known conversion for argument 1 from ‘algo1Concrete’ to ‘algo1Virtual&’
/some_path/algo/algocont.h:36:7: note: algoContainer::algoContainer(const algoContainer&)
/some_path/algo/algocont.h:36:7: note:   no known conversion for argument 1 from ‘algo1Concrete’ to ‘const algoContainer&’

为了便于阅读,我重命名了路径并插入了示例文件和类名(与上面相同)。只是备注:line 45 是相关构造函数的定义。另一方面,line 36 是行class algoContainer

我也试过这段代码:

algoContainer myDect((algo1Virtual)(algo1Concrete(workData)));

然后错误就完全不同了:

/some_path/main.cpp:47:86: error: cannot allocate an object of abstract type ‘algo1Virtual’
/some_path/algo/alg1/algo1virtual.h:31:7: note:   because the following virtual functions are pure within ‘algo1Virtual’:
/some_path/algo/alg1/algo1virtual.h:42:8: note:     virtual algo1Virtual::~algo1Virtual()
/some_path/algo/alg1/algo1virtual.h:39:18: note:    virtual void algo1Virtual::someAlgo1Function(std::vector<data>&)
/some_path/main.cpp:47:87: error: no matching function for call to ‘algoContainer::algoContainer(algo1Virtual)’
/some_path/main.cpp:47:87: note: candidates are:
/some_path/algo/algocont.h:45:5: note: algoContainer::algoContiner(algo1Virtual&, algo2Virtual&)
/some_path/algo/algocont.h:45:5: note:   no known conversion for argument 1 from ‘algo1Virtual’ to ‘algo1Virtual&’
/some_path/algo/algocont.h:36:7: note: algo1Virtual::algo1Virtual(const algo1Virtual&)
/some_path/algo/algocont.h:36:7: note:   no known conversion for argument 1 from ‘algo1Virtual’ to ‘const algo1Virtual&’

希望这会有所帮助。

【问题讨论】:

  • 你能告诉我们你从algoContainer myAC((algo1Concrete(workData)));得到的错误吗?
  • 正如Kerrek 提到的,这是一个最令人烦恼的解析 问题。如果您在使用括号消除歧义时遇到错误,则错误消息将有助于解决问题。
  • @Akanksh 我知道这是一个很长的问题,但我提到的最后一个链接详细解释了最令人头疼的解析,我在问这个问题时就知道了。我从来没有在我的 Q 中明确使用过“最令人烦恼的解析”这个词,但现在我已经编辑了它并将它添加到问题的标签中。
  • @penelope 抱歉抢先使用该链接。我认为这个问题可能是由于 algoContainer 构造函数将输入作为非常量引用,它不能绑定到匿名临时对象。您将需要命名变量,或通过右值引用获取参数,并将 algoConcrete “移动”到其中。本质上,想想谁“拥有”这些论点,以及他们的一生应该是怎样的。

标签: c++ constructor function-declaration most-vexing-parse


【解决方案1】:

问题似乎是由于构造函数采用的参数:

algoContainer( algo1Virtual &alg1,
               algo2Virtual &alg2 );

注意:为简洁起见,我删除了默认参数。

这将参数作为非常量引用。因此,当您拨打以下电话时:

algoContainer myAC(algo1Concrete(workData));

构造:

algo1Concrete(workData)

导致构建一个匿名临时。匿名临时对象不能绑定到非常量引用,这很简单,因为它们是临时的,您可能对它们所做的任何更改都会立即消失(这不是真正的原因,但似乎是有道理的。修改匿名并不意味着任何临时的,因为您以后无法使用它(没有名字)或最终(它的临时))。实际上,非常量引用只能绑定到左值,而匿名临时对象是右值。 (详情:Non-const reference may only be bound to an lvalue

一般来说,这种使用意味着人们想要将正在构造的对象的完全所有权赋予函数。这可以通过按值传递(昂贵)或在 C++11 中通过右值引用传递来完成。

按值传递将如下所示:

algoContainer( algo1Virtual alg1,
               algo2Virtual alg2 );

这将导致不必要的副本。

另一种选择是在 C++11 中通过右值引用传递,例如:

algoContainer( algo1Virtual &&alg1,
               algo2Virtual &&alg2 );

现在您的第一次使用将开箱即用:

std::vector <data> workData;
// fill workData
algoContainer myAC(algo1Concrete(workData));
myAC.someUsefulFunction();

但是您的第二次使用需要修改,以便您的对象被“移动”到构造函数中,并且 algoContainer 获得数据的所有权(名称局部变量然后是“坏的”并且根本不应该使用。

std::vector <data> workData;
// fill workData
algo1Concrete myA1(workData);
algoContainer myAC(std::move(myA1)); //NOTICE THE std::move call.
//myA1 is now a dummy, and unusable as all the internals have gone.
myAC.someUsefulFunction(); 

要使上面的示例正常工作,您必须使用以下签名为 algo1Concrete 实现移动构造函数:

algo1Concrete ( algo1Concrete&& other )

这会将内部结构简单地转移到当前状态,并使“其他”处于未定义状态。 (详情:http://msdn.microsoft.com/en-us/library/dd293665.aspx

注意:关于默认参数。

我通常建议避免函数的默认参数,因为它们会导致更多的混乱而不是方便。所有默认参数都可以简单地通过重载函数来“模拟”。因此,在您的情况下,您将拥有三个 ctor:

algoContainer(); //This assumes that the args were both the statics
algoContainer( algo1Virtual alg1 ); //This assumes that arg2 was the static.
algoContainer( algo1Virtual alg1, algo2Virtual alg2 ); //This uses both input.

我同意它更冗长,而且目前没有很多编译器实现继承构造函数,所以我们也经常复制代码。但这会将一个问题与调查问题时弹出的许多调试/神奇值问题隔离开来。但是,FWIW,这只是一个意见。

【讨论】:

  • 当我要求你扩大一点时,我什至没有想到这个 :) 这太棒了,谢谢 :D
【解决方案2】:

algoContainer myAC(algo1Concrete(workData));

这条线是非法的。您不能将右值绑定到可变的左值引用。它必须是const-,即使是这样,对象也会在被构造函数以外的任何函数使用之前就死掉了。

【讨论】:

  • +1 您发现了一旦修复了最令人头疼的解析就会出现的问题,即使没有错误消息。
  • 这似乎是正确的。您必须按值或(如 DeadMG 所说)按 const& 接受参数。
  • @DeadMG 我想我明白你在说什么......而且我知道这条线是非法的,这就是让我发布问题的原因。但问题是,我希望使用此代码的方式,构造的 algo1Concrete 是必需的,并且仅由它所构造的 algoContainer 和在其中使用,但是 algo1Concrete 对象 正在在使用过程中被改变。我对另一种方法很感兴趣,我可以只用 1 行来做到这一点。
  • @penelope 您可以将值传递给构造函数。在不知道 algoContainer 的构造函数的具体内容的情况下,可能还有其他替代方案。
  • @penelope:由于生命周期,您别无选择。您必须动态分配它,或者只要algoContainer 存在,就让它在堆栈中。
【解决方案3】:

作为当前解决方案的替代方案,您可以使用额外的一对括号,也可以使用复制初始化。

【讨论】:

    【解决方案4】:

    这样写:

    algoContainer myAC((algo1Concrete(workData)));
    

    然后在互联网上搜索“最令人头疼的解析”。 StackOverflow 上也有数百个此问题的重复项,但很难找到,因为问题本身从未发现问题。 (如果是,那就没有问题了。)


    (编辑后:)临时对象(例如由按值返回的函数调用创建的对象)不绑定到非常量引用。你需要说:

    algo1Concrete ac = algo1Concrete(workData);
    algoContainer myAC(ac);
    

    【讨论】:

    • OP 已经说过这会导致一系列其他错误(尽管我希望括号是解决方案)。
    • @Kerrek SB 我已经在我的问题中包含了一个链接,解释了最令人烦恼的解析,正如我在发布它时所知道的那样。我没有在 Q 中明确使用“最令人烦恼的解析”这个词,但我现在编辑了这些词。但是,这仍然不是我的主要问题。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-03-25
    • 2022-11-21
    • 2014-08-01
    • 2019-11-05
    • 2015-07-02
    相关资源
    最近更新 更多