【问题标题】:Error while calling constructor from another constructor in C++从 C++ 中的另一个构造函数调用构造函数时出错
【发布时间】:2013-03-15 17:38:05
【问题描述】:

我有一个struct A,它有几个初始化了不同数据成员的构造函数。

template<typename T>
    struct A {

    typedef std::vector<T> type1
    type1 a;
    type1 b;
    type1 c;

    A(type1 i_a): a(i_a) {
    }

    A(type1 i_a, type1 i_b): A(i_a), b(i_b) {
    }

    A(type1 i_a, type1 i_b, type1 i_c): A(i_a, i_b), c(i_c) {
    }
};

我得到的错误是当我用 custom_type 实例化它时,错误是 type A&lt;custom_type&gt; is not direct base of A&lt;custom_type&gt; 突出显示我在另一个构造函数中调用的构造函数。我正在使用 C++11。有什么问题?

【问题讨论】:

  • 什么编译器和选项?看起来你没有使用 C++11,即使你打算这样做。
  • 可能是您的编译器不支持委托构造函数。您使用的是哪个编译器和版本?
  • 我使用g++ -std=c++0x,如果这是你的意思?并且版本是4.6
  • 我不确定是否允许将对其他构造函数的调用与成员的直接初始化结合起来——如果你删除 bc 的 init 就可以了

标签: c++ c++11 constructor ctor-initializer


【解决方案1】:

构造函数可以初始化它的基类和成员,或者委托给同一类的另一个构造函数,而不是两者。

A(i_a) 构造一个完整的A 对象,复制成员a 和默认构造bc。所以拥有A(type1 i_a, type1 i_b): A(i_a), b(i_b) {} 是没有意义的——第一个初始化器已经初始化了b。你可以改为

A(type1 i_a, type1 i_b) : A(i_a) { b = std::move(i_b); }

【讨论】:

  • 就是这样:"如果一个mem-initializer-id指定了构造函数的类,它应该是唯一的mem-initializer;构造函数是一个委托构造函数,而mem-initializer选择的构造函数是目标构造函数。”从 15.6.2p6
  • 而这个问题就是为什么通常最好将参数较少的构造函数委托给具有更多参数的构造函数;将所有内容委托给最通用的构造函数,而专门的构造函数只需将默认值发送给更通用的构造函数即可完成工作。
  • 我不确定“所以它没有意义 A(type1 i_a, type1 i_b): A(i_a), b(i_b) {} -第一个初始化程序已经初始化了 b"。听起来像是初始化对象并在此之后覆盖 b 值的合理情况。就 C++ 标准而言,它是否存在问题,这是另一个问题
  • @icepack:Mem-initializers 导致调用构造函数。每个(子)对象只调用一个构造函数。覆盖将是一个赋值,而不是一个初始化。
  • 我知道,我只是说在语言中启用这样的操作并不是一个不合理的要求-例如,如果mem-initializer跟随构造函数调用,编译器可以省略该字段的初始化并将其延迟到 mem-initializer 调用。
【解决方案2】:

aschepler 提供了答案,但我想解释导致问题的原因,并展示委托构造函数以避免它的“最佳实践”。

在您的代码中,您有一系列构造函数,每个构造函数都比它们委托给的构造函数更通用。那就是你试图让一个构造函数委托给一个更专业的构造函数,它不会做任何事情,然后你需要做一些工作来处理额外的通用性。这与支持它的早期语言中被证明是最实用的构造委托方式相反。

您想要的是让最通用的构造函数成为委托的“根”(“指定初始化程序”或“指定构造函数”,在其他语言中)。更专业的构造函数将使用更通用的构造函数来完成它们的工作,将数据传递给它们以供处理特殊情况。

在您的情况下,特殊行为是对某些成员使用默认值,而不是从用户那里获取初始值。因此,您的更专业的构造函数将通过传递它们给出的参数以及其他成员的默认值来完成它们的工作。

template<typename T>
struct A
{
    typedef std::vector<T> type1;
    type1 a;
    type1 b;
    type1 c;

    A(type1 i_a, type1 i_b, type1 i_c): a(i_a), b(i_b), c(i_c) {}

    A(type1 i_a, type1 i_b): A(i_a, i_b, {}) {}

    A(type1 i_a): A(i_a, {}) {}
};

【讨论】:

  • 有趣。感谢您的解决方案。我会将此标记为答案,但我想现在取消标记另一个是不礼貌的。
  • {} 代替额外参数表示什么?
  • @user592748 这是对这些参数的默认构造值的统一初始化语法。
【解决方案3】:

但你仍然可以从构造函数的主体中调用同一类的不同构造函数(如果出于某种原因你想要的话)

class CComplex{
public:
    CComplex(int real1,int image1,char c)
    {
        cout<<"RealImg";
        real=real1;
        image=image1;
        char* x; char xc=c;x=&xc;
        void* v;
        f(x);
        CComplex ccc(x,v); //this is OK
        CComplex cccc(1,2,3); //as this too
    }
    CComplex():real(0),image(0){cout<<"DEFAULT";}
    CComplex(const CComplex &c)
    {
        real=c.real;
        image=c.image;
        cout<<"COPY";
    }
    CComplex& operator=(CComplex const& ref){ 
        cout<<"ASSIGN";
        //CComplex* c;
        CComplex cobj(43,45,'x');
        //c=&cobj;
        //CComplex* c=new CComplex(44,45); 
        return cobj;
    }

    CComplex(int i1, int i2, int i3){cout<<"\n123!";}
    CComplex(const char* y,void* v){cout<<"\nCrefvoid!";}
    ~CComplex(){cout<<"\n~CComplex(){}";}
public:
    void Display(void)
    {
        cout<<real<<"+"<<image<<"i"<<endl;
    }
    static bool CComplexComparator(CComplex c1, CComplex c2){return true;}
    static void CComplexInit(CComplex& c){
        c.real=100;
    }
    int real,image;
}; 

【讨论】:

  • 声明ccccccc 的语句当然不能像委托构造函数那样帮助初始化原始CComplex 对象。
  • 这与问题有什么关系?.. 无论如何,这与从函数内部(甚至同一个)调用另一个版本的重载函数没有什么不同
  • 我想表明仍然可以调用不同版本的构造函数。可能
  • 这些是根本不同的。您的内部构造函数调用涉及完全不同的对象。
猜你喜欢
  • 1970-01-01
  • 2011-03-24
  • 1970-01-01
  • 1970-01-01
  • 2010-12-15
  • 1970-01-01
  • 2014-03-12
  • 2012-06-22
  • 2010-09-23
相关资源
最近更新 更多