我有两个类,A 和 B,B 派生自 A。A 有多个构造函数(下例中为 2 个)。 B 有一个额外的成员要初始化(它有一个默认的初始化程序)。
我怎样才能实现 B 可以使用 A 的构造函数之一来构造,而不必手动重写 B 中 A 的所有构造函数重载?
(In the example below, I would otherwise have to provide four constructors for B: B():A(){}
, B(string s):A(s){}
, B(int b):A(),p(b){}
, B(string s, int b):A(s),p(b){}
, instead只有两个,至少在忽略默认参数的可能性时)。
我的方法是完美转发,但是,以下情况会导致错误:
#include <utility>
#include <string>
struct A {
A(const std::string& a) : name(a) {}
A(){}
virtual ~A(){}
std::string name;
};
struct B : public A {
template<typename... Args>
B(Args&&... args) : A(std::forward<Args>(args)...) {}
B(const std::string& a, int b) : A(a), p(b) {}
int p = 0;
};
int main()
{
B b1("foo");
B b2("foobar", 1);
}
对于 b2,GCC 抱怨 no matching function for call to 'A::A(const char [5], int)
。显然它正在尝试调用完美的转发构造函数,这显然不应该工作,而不是 B 的第二个构造函数。
为什么没有看到编译器第二个构造函数而是调用它呢?编译器找不到 B 的正确构造函数是否存在技术原因?我该如何解决这种行为?
确切的错误信息:
main.cpp: In instantiation of 'B::B(Args&& ...) [with Args = {const char (&)[5], int}]':
main.cpp:26:19: required from here
main.cpp:15:54: error: no matching function for call to 'A::A(const char [5], int)'
B(Args&&... args) : A(std::forward<Args>(args)...) {}
^
main.cpp:6:5: note: candidate: A::A()
A(){}
^
main.cpp:6:5: note: candidate expects 0 arguments, 2 provided
main.cpp:5:5: note: candidate: A::A(const string&)
A(const std::string& a) : name(a) {}
^
main.cpp:5:5: note: candidate expects 1 argument, 2 provided
main.cpp:4:8: note: candidate: A::A(const A&)
struct A {
^
main.cpp:4:8: note: candidate expects 1 argument, 2 provided
原文由 mrspl 发布,翻译遵循 CC BY-SA 4.0 许可协议
"foobar"
是const char (&) [7]
。因此Args
比const std::string&
更好的匹配因此,选择了这个重载:
其中
Args
是const char (&) [7]
所以它变成:
它被转发到
A
的 2 参数构造函数……它不存在。像这样的东西……
简而言之(并且说得很简单),当发生重载决议时,编译器会执行以下操作:
展开所有可能匹配给定参数的模板化重载。将它们添加到列表中(权重表示到达那里所涉及的专业化水平)。
将任何具体的重载添加到列表中,这些重载可以通过合法地将转换运算符应用于参数来实现,权重指示需要多少次转换才能将提供的参数转换为函数参数类型。
按升序“工作”或权重对列表进行排序。
选择需要最少工作的那个。如果有一个最适合的平局,那就错了。
编译器对此进行了尝试。这不是递归搜索。
我提前向我们中间的纯粹主义者道歉,他们会觉得这种幼稚的解释令人反感:-)