假设在命名空间operator*
中的某处定义了免费的ns
:
namespace ns {
struct Dummy {};
template <typename T>
Dummy operator*(T&&) {
return {};
}
}
在另一个地方,同一名称空间中有一个基类,它定义了成员operator*
:
namespace ns {
template <typename T>
struct Base {
T x;
T& operator*() {
return x;
}
};
}
很多类型都源自它。它们的行为相同,但必须是不同的,因为其他模板需要专门针对它们:
namespace ns {
template <typename T>
struct DerivedA : Base<T> {};
template <typename T>
struct DerivedB : Base<T> {};
template <typename T>
struct DerivedC : Base<T> {};
// etc
}
当我尝试在派生类上使用operator*
时:
ns::DerivedA<int> d;
*d = 42;
海湾合作委员会对我大吼大叫&#34;傻瓜!您没有将int
分配给Dummy
!&#34;,这显然意味着使用免费operator*
而不是基类中的成员。
我对free运算符没有任何控制权,也无法将派生类移动到不同的名称空间。
如何解决此问题而不在每个派生类中复制operator*
?
答案 0 :(得分:2)
简短的回答,你可以这样做:
template <typename T>
struct DerivedA : Base<T> {
using Base<T>::operator*;
};
答案很长:为了找出要调用的*d
,我们必须确定所有可行的函数(§13.3.2):
从为给定上下文(13.3.1)构造的候选函数集合中,有一组可行的函数 选择,通过比较参数的转换序列,从中选择最佳函数 最合适(13.3.3)。可行功能的选择考虑了参数和功能之间的关系 转换序列排名以外的参数。
有两个:
template <typename T>
Dummy operator*(T&& );
T& Base::operator*();
为了找出要选择哪一个,我们必须确定哪个“隐式转换序列”(第13.3.3.1节)更好:
隐式转换序列是用于转换函数调用中的参数的转换序列 到被调用函数的相应参数的类型。
我们的第一个选项是“完全匹配”,而我们的第二个重载选项是(§13.3.3.1.6):
当参数具有类类型且参数表达式具有派生类类型时, 隐式转换序列是从派生类到基类的派生到基础的转换。 [注意:没有这样的标准转换;此派生到基础转换仅存在于描述中 隐式转换序列。 - 结束注释]派生到基础转换具有转换等级
转换序列的排名是(§13.3.3.1.1.3):
表12中的每次转化也都有相关的排名(完全匹配,促销或转化)。这些是 用于对标准转换序列进行排名(13.3.3.2)。转换序列的等级由确定 考虑序列中每个转换的等级和任何参考约束的等级(13.3.3.1.4)。如果 其中任何一个都有转换排名,序列有转换排名;否则,如果其中任何一个有促销 等级,序列有晋级等级;否则,序列具有完全匹配等级。
我不知道如何在这里插入表格。但基本上我们有一个“完全匹配”(调用Dummy operator*(T&&)
)和一个“转换”(调用T& Base::operator*
),因此“完全匹配”是“最佳可行功能”。 (§13.3.3.2):
如果只有一个可行功能比所有其他可行功能更好,那么它就是 一个由重载决议选择的
这就是Dummy operator*(T&& )
首选的原因。
现在,为什么我的提案有效?在这种情况下,我们的两个选项是:
template <typename T>
Dummy operator*(T&& );
T& DerivedA<int>::operator*();
所以我们有两个“完全匹配”候选者 - 虽然其中一个是通过模板,选择更好的可行功能的标准之一是(§13.3.3.1):
鉴于这些定义,可行函数F1被定义为比另一个可行函数更好的函数 F2如果......
- F1是非模板功能,F2是功能模板专业化
因此,在这种情况下,我们选择DerivedA::operator*
。这是你想要的。
答案 1 :(得分:0)
我对free运算符没有任何控制权,也无法将派生类移动到不同的名称空间。
如果你不能从用户的角度将派生类移动到不同的命名空间,但可以在代码本身中移动它们,你可以这样做:
namespace ns {
namespace ns_ {
template <typename T>
struct Base {
T x;
T& operator*() { return x; }
};
template <typename T>
struct DerivedA : Base<T> {};
}
using namespace ns_;
}