我无法理解为什么我的模板层次结构不能通过比一层更深的构造函数参数。
到目前为止,我已经读过它可能与命名空间或未命名的模板构造函数有关。我使用范围指令将构造函数参数传递给基类的各种尝试都失败了。
// 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 :(得分:3)
当你虚拟地继承时,你会问你继承的类型是&#34; singleton&#34;在对象heiarchy中(不是程序范围的单例,而是一个实例范围的单例,看起来很奇怪)。
作为副作用,实际最派生类负责构造虚拟基类,而不管存在哪些中间类。
虚拟继承是指您需要多次从某个类继承,但确保只存在一个实例。 C ++的设计者没有计算出一些复杂的规则来确定哪个构造函数调用是被调用者,而是要求最派生的类(实际创建的类)确切地指定了什么构造函数被调用。
其他人都可以插入,但只有当 特定类(而不是更多派生类)的实际实例被创建时,他们的意见才会重要。
virtual
virtual
继承几乎与virtual
方法/成员函数无关。除非你需要二进制布局或钻石继承的原因,否则不要使用它。
简单地从你的代码中消除它,如果你从不从一个类继承到2个不同的路径到同一个派生类,你就不会错过它。
class DerivedOne : public Base<std::string>
和
class DerivedTwo : public DerivedOne
并且您的代码会编译并执行您想要的内容。
答案 1 :(得分:1)
当您使用虚拟继承时,始终是MOST DERIVED类初始化虚拟基础。即使您的中间类传递了一个值,它也不会在其他派生类中执行此操作,这些类必须直接初始化Base。
由于你的派生类(DerivedTwo)没有直接初始化Base,所以Base初始化时没有传递给它的任何参数,因此需要一个默认的构造函数。
class DerivedTwo : public virtual DerivedOne
{
public:
DerivedTwo(const std::string& path) : Base(path), DerivedOne(path) {}
}
或者,停止使用虚拟继承,只需使用&#34; normal&#34;遗产。也就是说,改变这个:
class DerivedOne : public virtual Base<std::string>
到此:
class DerivedOne : public Base<std::string>
当然,除非你真的需要虚拟继承。 :)