gpt4 book ai didi

c++ - 模板化运算符重载决议,成员函数与非成员函数

转载 作者:塔克拉玛干 更新时间:2023-11-02 23:43:42 27 4
gpt4 key购买 nike

在试用 clang-3.4(从 gi​​t 编译)时,它无法编译我的一个项目,提示在解析重载运算符时存在歧义。我发现有两个模板化运算符,其中一个被声明为成员函数,另一个被声明为非成员函数,两者看起来同样匹配。

以下SSCCE演示情况:

#include <iostream>

struct ostr {
std::ostream& s;

template<class T>
ostr& operator<<(const T& x) { s << x; return *this; }
};

struct xy {
double x, y;
};

template<class Stream>
Stream& operator<<(Stream& s, const xy& x) {
s << "[" << x.x << ", " << x.y << "]";
return s;
}

int main() {
ostr os{std::cout};
xy x{4, 5};
os << "Value is: " << x <<"\n";
}

该项目之前编译得很好,我用几个编译器(gcc 4.54.64.74.8clang 3.3)再次检查了这个 SSCCE,它们都在没有任何警告的情况下编译了它(-Wall -Wextra -pedantic)。所有编译器都设置为 C++11/C++0x 标准。将 ctor 添加到 ostr 后,即使在 MSVC 20122010 上也能正常编译)

使两个 operator<< 都成为非成员会在所有编译器中表现出歧义(正如预期的那样)

在查看标准草案(N3242N3690)后,我没有发现任何使成员函数/运算符比非成员函数/运算符更匹配的东西。

所以我没能证明clang-3.4是错的,我想知道谁是对的。因此我的问题是:

  • 此代码有效吗?成员运算符/函数是否应该比非成员运算符/函数更好地匹配,这是 clang-3.4 中的错误?
  • 还是所有其他编译器都错误/过于宽松?

我知道将第二个 operator<< 更改为非模板函数(使用 std::ostream 而不是模板参数)将解决歧义并按预期工作,但这不是这里的重点。

最佳答案

重载决议为成员函数添加一个额外的参数只是为了重载决议:

[over.match.funcs]/2

The set of candidate functions can contain both member and non-member functions to be resolved against the same argument list. So that argument and parameter lists are comparable within this heterogeneous set, a member function is considered to have an extra parameter, called the implicit object parameter, which represents the object for which the member function has been called.

/4

For non-static member functions, the type of the implicit object parameter is

— “lvalue reference to cv X” for functions declared without a ref-qualifier or with the & ref-qualifier

— “rvalue reference to cv X” for functions declared with the && ref-qualifier

where X is the class of which the function is a member and cv is the cv-qualification on the member function declaration.

遵循一些特殊规则,例如允许将右值绑定(bind)到此隐式对象参数(用于调用不带右值引用限定符的成员函数,例如 ostr{std::cout}<<"hello")。


函数签名包括我们需要比较重载决议的隐式对象参数是:

template<class T>
ostr& ostr::operator<<(ostr&, const T&); // F1

template<class Stream>
Stream& ::operator<<(Stream&, const xy&); // F2

替换为 os << x 后,我们得到了相同的签名:

ostr& ostr::operator<<(ostr&, const xy&);
ostr& :: operator<<(ostr&, const xy&);

因此只有 [over.match.best]/1 中的“决胜局”之一可以解决歧义。事实上,可以应用,即“F1F2 更专业”(反之亦然):函数模板的部分排序。

注意添加隐式对象参数的过程在偏序[temp.func.order]/3 的描述中再次指定。


对于 F1 的部分订购和 F2 (如上定义),我们首先创建两个独特的类型:

struct unique_T {};
struct unique_Stream {};

然后我们转换F1进入F1'通过替换模板参数 T具有独特的类型 unique_T (对于 F2 也是如此):

ostr& ostr::operator<<(ostr&, const unique_T&);
ostr& :: operator<<(unique_Stream&, const xy&);

转换函数的参数F1'现在用于尝试推断未转换的模板参数 F2 :

ostr a0;
unique_T a1; // no reference, no cv-qualifier
::operator<<(a0, a1) // does template argument deduction succeed?

// reminder: signature of ::operator<<
template<class Stream>
Stream& ::operator<<(Stream&, const xy&);

a0扣除成功[与Stream = ostr ],因此类型 ostr&来自 F1被认为至少与 F2 的相应第一个参数的类型一样专业( Stream&Stream 是模板参数)。我不确定第二个参数会发生什么 a1 , 因为 ::operator<< 的第二个参数没有扣除(它是 const xy& 类型)。

现在我们用来自 F2' 的参数重复这个过程并尝试推导出 F1 的模板参数:

unique_Stream a0;
xy a1;
ostr::operator<<(a0, a1);

// reminder: signature of ostr::operator<<
template<class T>
ostr& ostr::operator<<(ostr&, const T&);

在这里,第一个参数没有推导发生,但第二个参数发生并成功 [with T = xy ].

我的结论是没有比这更专业的函数模板了。因此,由于歧义,重载解析应该失败

关于c++ - 模板化运算符重载决议,成员函数与非成员函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19955954/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com