为什么模板成员运算符会被选中,而不是外部友元运算符?
情形如下:
struct Builder
{
Builder & append(int);
Builder & append(double);
// ... many other overloads for non-object types
Builder & operator<<(auto && t) { return append(t); }
};
struct X
{
friend Builder & operator<<(Builder & p, X const &) { /* … */ }
};
int main()
{
Builder p;
X x;
p << x;
}
编译器(MSVC和 GCC)在选择成员运算符重载时,会倾向于使用 template 的那个,而不是 friend 的。为什么?
解决方案
要理解“为什么”,请记住,auto 参数类型本质上是模板的简写,因此你的代码大致相当于以下内容:
Builder & append(int);
Builder & append(double);
template <class T>
Builder & operator<<(T && t) { return append(t); } // [1]
};
由于它调用 append,T 必须是 int、double,或某种能够隐式转换为 int 或 double 的类型。由于不存在从 X 转换到 int 或 double 的转换,因此会失败。我们可以通过添加从 X 转换到 int 的转换来让它起作用:
struct X
{
friend Builder & operator<<(Builder & p, X const &) { return p << 42; }
operator int() { return 1; }
// conversion to `double` would work just as well.
};
...或者通过添加一个接收 X 的 append 的重载来实现:
struct X;
struct Builder
{
Builder & append(int);
Builder & append(double);
Builder & append(X const &);
// ... many other overloads
Builder & operator<<(auto && t) { return append(t); } // [1]
// ...
两种方式都能工作。在这种情况下,你可以同时拥有两者,但如果你得到的是等价的转换序列,就会产生歧义,编译就无法通过。
站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。