C++声明函数,而不是调用复杂的构造函数

C++ declares a function instead of calling a complex constructor

本文关键字:调用 复杂 构造函数 声明 函数 C++      更新时间:2023-10-16

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

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

以下是我的代码片段(我重命名了类名,因为它们很长且不相关(:

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

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

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

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

我可以按原样使用代码,但如果有人能为我现在使用的代码提供更优雅的解决方案,我们将不胜感激。


编辑:当我修复最烦人的解析时,我得到的错误消息

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

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( algo1Virtual &alg1,
               algo2Virtual &alg2 );

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

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

algoContainer myAC(algo1Concrete(workData));

建设:

algo1Concrete(workData)

导致构建一个匿名临时。匿名临时不能绑定到非常量引用,很简单,因为它们是临时的,您可能对它们所做的任何更改都会立即消失(这不是真正的原因,但似乎是有道理的。修改匿名临时并不意味着什么,因为您无法在以后(没有名称(或最终(临时(使用它。实际上,非常量引用只能绑定到 l 值,而匿名临时引用是 r 值。(详细信息:非常量引用只能绑定到左值(

通常,这种用法意味着人们希望将正在构造的对象的完全所有权赋予函数。这可以通过按值传递(昂贵(或在 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,这只是一种意见。

algoContainer myAC(algo1Concrete(workData((;

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

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

写这个:

algoContainer myAC((algo1Concrete(workData)));

然后在互联网上搜索"最令人烦恼的解析"。在StackOverflow上也有数百个重复的问题,但它们很难找到,因为问题本身从未被发现。(如果是这样,那就毫无疑问了。


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

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