当前位置: 首页 > 知识库问答 >
问题:

模板化运算符重载解析,成员与非成员函数

赖诚
2023-03-14

以下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";
}

项目之前编译得很好,我用几个编译器(gcc4.54.64.74.8clang3.3)再次检查了这个SSCCE,所有这些编译器都在没有任何警告的情况下编译了它(使用-wall-wextra-pedantic)。所有编译器都设置为C++11/C++0x标准。将ctor添加到OSTR中后,即使在MSVC20122010上也能很好地编译

使两个运算符<<的非成员都显示了所有编译器中的二义性(正如预期的那样)

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

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

  • 此代码有效吗?成员运算符/函数应该比非成员运算符/函数更匹配吗?这是CLANG-3.4中的一个bug?
  • 还是所有其他编译器都错误/权限太大?

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

共有1个答案

满昊然
2023-03-14

重载解析向成员函数添加了一个附加参数,只是为了重载解析的目的:

[over.match.funcs]/2

候选函数集可以包含要针对同一参数列表解析的成员函数和非成员函数。为了使参数列表和参数列表在这个异类集合中是可比较的,一个成员函数被认为有一个额外的参数,称为隐式对象参数,它表示为其调用成员函数的对象。

对于非静态成员函数,隐式对象参数的类型为

-“lvalue reference to cvx”,用于不使用ref-qualifier或使用&ref-qualifier声明的函数

-对于使用&ref-qualifier声明的函数,“rvalue reference to cvx

其中x是函数作为其成员的类,cv是成员函数声明上的cv限定。

下面有一些特殊的规则,例如允许将rvalue绑定到这个隐式对象参数(用于调用rvalue上的成员函数,不使用ref-qualifier,例如ostr{std::cout}<<“hello”)。

我们需要比较用于重载解析的函数签名(包括隐式对象参数)如下:

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

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

替换os< 后,得到相同的签名:

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

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

请注意。在部分排序[temp.func.order]/3的说明中再次指定了添加一个隐式对象参数的过程。

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[withstream=ostr]的推导成功,因此,来自f1的类型ostr&被认为至少与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&);

在这里,对于第一个参数不发生推演,但是对于第二个参数发生并成功[witht=xy]。

我的结论是,没有哪个函数模板更专业。因此,重载解析应该会因为二义性而失败。

 类似资料:
  • 我有一个这样的类: 返回从town参数到路径的。 我试图做的是为优先级队列重载布尔运算符,但它给了我错误 我知道为什么它会给我这个错误,但是我不知道如何在< code>Comparator结构中使用一个非静态函数。 > 必须是非静态的 我尝试在类中移动但没有成功 我需要用一个结构重载运算符,因为我需要在上使用两个不同的bool重载,用于两种不同的情况,但参数相同(两个城镇)。换句话说,我需要结构,

  • 我试图重写模板类http://docs.ros.org/hydro/api/rviz/html/c/message__filter__display_8h_source.html使用多种消息类型,使用变量模板。 我的第一个问题是如何使用可变模板重写下面的示例代码,以便它可以用于任意数量的模板参数,而不仅仅是2个。 我需要在家长类: 每个模板类型的虚拟成员函数 每个模板类型的每个的成员函数 每个模板

  • 本文向大家介绍C++中的四个默认成员函数与运算符重载详解,包括了C++中的四个默认成员函数与运算符重载详解的使用技巧和注意事项,需要的朋友参考一下 本文主要给大家介绍了关于C++默认成员函数与运算符重载的相关内容,分享出来公的敬爱啊参考学习,话不多说,来一起看看详细的介绍: 一:类和对象的基础知识:类的定义,访问限定符,面向对象封装性,对象的大小计算等等。(编译环境为VS2015) 面向对象程序设

  • C++ 重载运算符和重载函数 类成员访问运算符( -> )可以被重载,但它较为麻烦。它被定义用于为一个类赋予"指针"行为。运算符 -> 必须是一个成员函数。如果使用了 -> 运算符,返回类型必须是指针或者是类的对象。 运算符 -> 通常与指针引用运算符 * 结合使用,用于实现"智能指针"的功能。这些指针是行为与正常指针相似的对象,唯一不同的是,当您通过指针访问对象时,它们会执行其他的任务。比如,当

  • 我有一个问题,我想在下面的代码中专门化模板类的模板成员函数。这个问题的答案是模板类成员函数的显式特化,这似乎表明它无法完成。这是正确的吗,如果是这样,我可以使用任何解决方法,以便在编译时通过内联inc函数进行扩展? 非常感谢! g吐槽道: test2.cpp:32:13: 错误: 非命名空间作用域中的显式专用化 'struct IdxIterator' test2.cpp:32:25: 错误: 非

  • 我一直试图理解C++选择模板的方式。即,考虑以下代码示例: 前两个函数(test1)工作正常(为什么?): 一个常见的错误是声明两个仅在默认模板参数上不同的函数模板。这是非法的,因为默认模板参数不是函数模板签名的一部分,并且用相同的签名声明两个不同的函数模板是非法的。 所以看起来是这样的。但是,我看不出与前两个函数有太大的不同,前两个函数也有默认的模板参数。因此,我们对默认值(test1-work