2017-07-26 59 views
0

我很难理解为什么我的模板层次结构不会传递比一层更深的构造函数参数。模板层次结构构造函数参数

到目前为止,我读过它可能与名称空间或模板构造函数未命名有关。我通过范围指令将构造函数参数传递给基类的各种尝试都失败了。

// test.cc 
#include <string> 

// Base class, obviously. 
template <typename T> 
class Base { 
public: 
    Base(const T& t) : _t(t) {} 
    virtual ~Base() { } 

    virtual T getVal() const { return _t; } 

private: 
    T _t; 
}; 

// First derivation. This works, although it is explicit. 
class DerivedOne : public virtual Base<std::string> 
{ 
public: 
    DerivedOne(const std::string& path) : Base<std::string>(path) {} 
    virtual ~DerivedOne() {} 
}; 

// Second derivation. Constructor fails to compile unless I explicitly 
// pass the 'path' argument to Base in the initializer list or create a 
// default constructor for Base (which, of course, leaves Base::_t 
// uninitialized). 
class DerivedTwo : public virtual DerivedOne 
{ 
public: 
    DerivedTwo(const std::string& path) : DerivedOne(path) {} 
    // This works 
    // DerivedTwo(const std::string& path) : DerivedOne(path), Base(path) {} 
    virtual ~DerivedTwo() {} 
}; 

int main() 
{ 
    return 0; 
} 

编译器会抱怨:

test.cc: In constructor ‘DerivedTwo::DerivedTwo(const string&)’: 
test.cc:31:58: error: no matching function for call to ‘Base<std::__cxx11::basic_string<char> >::Base()’ 
    DerivedTwo(const std::string& path) : DerivedOne(path) {} 
                 ^
test.cc:7:5: note: candidate: Base<T>::Base(const T&) [with T = std::__cxx11::basic_string<char>] 
    Base(const T& t) : _t(t) {} 
    ^
test.cc:7:5: note: candidate expects 1 argument, 0 provided 
test.cc:5:7: note: candidate: Base<std::__cxx11::basic_string<char> >::Base(const Base<std::__cxx11::basic_string<char> >&) 
class Base { 
    ^
test.cc:5:7: note: candidate expects 1 argument, 0 provided 

为什么我需要声明一个默认的构造函数时,它似乎是参数的构造函数应该叫什么名字?为什么我必须显式地将DerivedTwo的参数传递给Base,因为我已将它传递给DerivedOne(谁应该将它传递给Base)?

有没有办法避免这种重复?这是否意味着当派生类初始化程序列表中的堆上分配了基本模板构造函数参数时,我不能使用初始化程序列表?

+0

这就是虚拟继承的工作原理。你是否真的想在'class DerivedOne:public virtual Base '和'class DerivedTwo:public virtual DerivedOne'中使用'virtual'?请注意,不需要使用虚拟功能。 – NathanOliver

+0

在真正的单词应用程序中,需要虚拟功能。我仍然不明白为什么它是必要的,也许我需要重新研究虚拟继承。 – vincent

+0

只有当您通过多重继承多次继承共同祖先时才需要虚拟继承。 – cababunga

回答

3

这是怎么回事

当你继承实际上,你是问你继承的类型是“单身”的对象heiarchy内(而不是计划范围的单例,而是一个实例范围内的单,看起来很奇怪)。

作为副作用,实际上大多数派生类负责构建虚拟基类,而不管存在哪些中间类。

虚拟继承的目的是当您需要从某个类多次继承,但确保只存在一个实例时。 C++的设计者没有制定出一套复杂的规则来确定哪个构造函数调用是被调用的,而是规定了派生最多的类(实际创建的类)决定了调用哪个构造函数。

其他人都可以参加,但只有在创建特定类别(而不是衍生更多的特定类别)的实际实例时才会产生他们的意见。

你可能不希望virtual

virtual继承几乎无关virtual方法/成员函数。除非需要二进制布局或钻石继承的原因,否则不要使用它。

简单地把它从你的代码中消除,如果你永远不会沿着2个不同的路径继承同一个派生类,那么你就不会错过它。

class DerivedOne : public Base<std::string> 

class DerivedTwo : public DerivedOne 

和你的代码编译并执行你仿佛想。

+0

谢谢你,我开始变得非常沮丧。这清除了我对虚拟继承的使用的明显误解! – vincent

+1

@vincent,是的,在这里使用相同的关键字是相当不幸的。 – SergeyA

1

当您使用虚拟继承它始终是最初派生类的初始化虚拟基地。即使你的中间类传递了一个值,它不会在其他派生类中这样做,它必须直接初始化Base。由于你的大部分派生类(DerivedTwo)不直接初始化Base,所以Base没有传递给它的任何参数就被初始化了,所以需要一个默认的构造函数。

class DerivedTwo : public virtual DerivedOne 
{ 
public: 
    DerivedTwo(const std::string& path) : Base(path), DerivedOne(path) {} 
} 

或者,停止使用虚拟继承,只使用“正常”继承。

class DerivedOne : public Base<std::string> 

当然除非,你真的需要虚拟继承:也就是说,改变这种:

class DerivedOne : public virtual Base<std::string> 

了这一点。 :)