为什么这里的重载决议是错误的?
why is the overload resolution wrong here?
假设我们的设置是这样的:
#include <iostream>
class Base {
private:
int a;
public:
Base(int a)
: a(a) {
}
virtual void print() = 0;
};
class Child : public Base {
public:
using Base::Base;
void print() override {
//some code
}
};
class Wrapper {
private:
void* base_ptr = nullptr;
public:
Wrapper(void* base_ptr)
: base_ptr(base_ptr) {
std::cout << "void* version called\n";
}
Wrapper(Base* const& base_ptr)
: base_ptr(base_ptr) {
std::cout << "Base*const& version called\n";
}
};
如果我们执行以下操作:
Child* child_ptr = new Child(1);
Wrapper w(child_ptr);
输出为:
Base*const& version called
这是意料之中的,完全正常。
但是如果我们将 Wrapper(Base *const&)
更改为 Wrapper(Base*&)
我们调用 void*
版本:
class Wrapper {
private:
void* base_ptr = nullptr;
public:
Wrapper(void* base_ptr)
: base_ptr(base_ptr) {
std::cout << "void* version called\n";
}
Wrapper(Base*& base_ptr)
: base_ptr(base_ptr) {
std::cout << "Base*const& version called\n";
}
};
int main() {
Child* child_ptr = new Child(1);
Wrapper w(child_ptr);
return 0;
}
对于这个版本,我们得到这个输出:
void* version called
但这是为什么呢? Base*const&
不应该让它也与临时对象兼容吗?
好的。经过一段时间的思考,我想通了。这仅仅是因为在我们尝试调用第二个版本的构造函数时发生了隐式转换,我们将 Child*
的类型更改为 Base*
并且因此它不再是 LValue
并且变为 RValue
,因此它不能绑定到 Base*&
,如果我们要将其签名更改为 Base*&&
,它会选择此重载(这是预期的,因为隐式转换发生在那里并使它成为 RValue
).
假设我们的设置是这样的:
#include <iostream>
class Base {
private:
int a;
public:
Base(int a)
: a(a) {
}
virtual void print() = 0;
};
class Child : public Base {
public:
using Base::Base;
void print() override {
//some code
}
};
class Wrapper {
private:
void* base_ptr = nullptr;
public:
Wrapper(void* base_ptr)
: base_ptr(base_ptr) {
std::cout << "void* version called\n";
}
Wrapper(Base* const& base_ptr)
: base_ptr(base_ptr) {
std::cout << "Base*const& version called\n";
}
};
如果我们执行以下操作:
Child* child_ptr = new Child(1);
Wrapper w(child_ptr);
输出为:
Base*const& version called
这是意料之中的,完全正常。
但是如果我们将 Wrapper(Base *const&)
更改为 Wrapper(Base*&)
我们调用 void*
版本:
class Wrapper {
private:
void* base_ptr = nullptr;
public:
Wrapper(void* base_ptr)
: base_ptr(base_ptr) {
std::cout << "void* version called\n";
}
Wrapper(Base*& base_ptr)
: base_ptr(base_ptr) {
std::cout << "Base*const& version called\n";
}
};
int main() {
Child* child_ptr = new Child(1);
Wrapper w(child_ptr);
return 0;
}
对于这个版本,我们得到这个输出:
void* version called
但这是为什么呢? Base*const&
不应该让它也与临时对象兼容吗?
好的。经过一段时间的思考,我想通了。这仅仅是因为在我们尝试调用第二个版本的构造函数时发生了隐式转换,我们将 Child*
的类型更改为 Base*
并且因此它不再是 LValue
并且变为 RValue
,因此它不能绑定到 Base*&
,如果我们要将其签名更改为 Base*&&
,它会选择此重载(这是预期的,因为隐式转换发生在那里并使它成为 RValue
).