在标头之间调用时,模板实例化无法匹配重载流运算符的参数列表

Posted

技术标签:

【中文标题】在标头之间调用时,模板实例化无法匹配重载流运算符的参数列表【英文标题】:Template instantiation unable to match argument list for overloaded stream operator when invoked between headers 【发布时间】:2016-09-30 10:45:06 【问题描述】:

注意:以前我没有遇到过这个问题!我的 to_string 方法工作得很好,模板实例化为我的 Point 类找到了正确的重载,我已经使用它几个月了!然而,最后一次提交出了点问题,在一个源文件中我收到了这个众所周知的错误消息(仅在这个源文件中,在其他地方 mx::to_string(mx::Point) 可以正常工作)。我无法弄清楚可能是什么原因,因为我没有注意到任何可能会搞砸的线路。

Plus 已经扫过所有有关流运算符重载的相关问题,但没有找到解决方案。

所以我有一个点类:

#ifndef MXBASIC_UTILITIES_H
#define MXBASIC_UTILITIES_H
#include <ostream>
#include <string>

namespace mx
    class Point;


class mx::Point

public:
    Point();
    Point(int x, int y);

    int getX() const;
    int getY() const;
    int& getX();
    int& getY();

    //...
;

inline mx::Point::Point() : _x(0), _y(0) 
inline mx::Point::Point(int x, int y) : _x(x), _y(y) 

inline int mx::Point::getX() const  return _x; 
inline int mx::Point::getY() const  return _y; 
inline int& mx::Point::getX()  return _x; 
inline int& mx::Point::getY()  return _y; 

// THE OVERLOAD:

inline std::ostream& operator<<(std::ostream& os, const mx::Point& point)
    return os << '(' << point.getX() << ',' << point.getY() << ')';


#endif

一个名为 helpertypes.h 的头文件,其中有我的 to_string 函数:

#ifndef HELPERTYPES_H
#define HELPERTYPES_H
#include <iostream>
#include <cstdint>
#include <memory>
#include <sstream>
#include <string>

// ...

namespace mx
template<typename T>
std::string to_string(const T& val)

    std::ostringstream os;

    os.flags(std::ios::fixed);
    os.precision(2);

    os << val; // getting error here as of recently, both in MinGW 4.7.3 and MSVC15

    return os.str();


#endif

完整的错误信息: 在这里阅读更多:pastebin.com/gFdFezrV

MSVC15:

2>c:(...)\classes\helpertypes.h(87): 错误 C2679: 二进制 ' c:\program files (x86)\microsoft visual studio 14.0\vc\include\ostream(495):注意: 可能是 'std::basic_ostream> &std::basic_ostream>::运算符 *)' 2> c:\程序文件(x86)\微软视觉工作室 14.0\vc\include\ostream(475): 注意: 或 'std::basic_ostream> &std::basic_ostream>::operator c:\program files (x86)\microsoft visual studio 14.0\vc\include\ostream(455):注意:或 'std::basic_ostream> &std::basic_ostream>::operator c:\program 文件 (x86)\microsoft visual studio 14.0\vc\include\ostream(435):注意:或 'std::basic_ostream> &std::basic_ostream>::operator c:\程序文件(x86)\微软视觉工作室 14.0\vc\include\ostream(415):注意:或 'std::basic_ostream> &std::basic_ostream>::operator c:\程序文件(x86)\微软视觉工作室 14.0\vc\include\ostream(395):注意:或 'std::basic_ostream> &std::basic_ostream>::操作符 c:\program files (x86)\microsoft visual studio 14.0\vc\include\ostream(375): note: or 'std::basic_ostream> &std::basic_ostream>::运算符 c:\program 文件 (x86)\microsoft visual studio 14.0\vc\include\ostream(355):注意:或 'std::basic_ostream> &std::basic_ostream>::操作符 c:\program 文件 (x86)\microsoft visual studio 14.0\vc\include\ostream(335):注意:或 'std::basic_ostream> &std::basic_ostream>::operator c:\程序文件(x86)\微软视觉工作室 14.0\vc\include\ostream(315):注意:或 'std::basic_ostream> &std::basic_ostream>::操作符 c:\program files (x86)\microsoft visual studio 14.0\vc\include\ostream(290):注意:或 'std::basic_ostream> &std::basic_ostream>::operator c:\程序文件(x86)\微软视觉工作室 14.0\vc\include\ostream(270):注意:或 'std::basic_ostream> &std::basic_ostream>::操作符 c:\program files (x86)\microsoft visual studio 14.0\vc\include\ostream(236):注意:或 'std::basic_ostream> &std::basic_ostream>::operator c:\程序文件(x86)\微软视觉工作室 14.0\vc\include\ostream(216):注意:或 'std::basic_ostream> &std::basic_ostream>::运算符 c:\程序文件(x86)\微软视觉工作室 14.0\vc\include\ostream(209):注意:或 'std::basic_ostream> &std::basic_ostream>::运算符 c:\program 文件 (x86)\microsoft visual studio 14.0\vc\include\ostream(202):注意:或 'std::basic_ostream> &std::basic_ostream>::运算符 &(__cdecl *)(std::basic_ios> &))' 2> c:\program files (x86)\microsoft visual studio 14.0\vc\include\ostream(196): 注意:或 'std::basic_ostream> &std::basic_ostream>::运算符 &(__cdecl *)(std::basic_ostream> &))' 2> c:\program files (x86)\microsoft visual studio 14.0\vc\include\ostream(692): 注意:或 'std::basic_ostream> &std::运算符 (std::basic_ostream> &,const char *)' 2> c:\program files (x86)\microsoft visual studio 14.0\vc\include\ostream(739): 注意: 或 'std::basic_ostream> &std::operator (std::basic_ostream> &,char)' 2> c:\program 文件 (x86)\microsoft visual studio 14.0\vc\include\ostream(777): 注意: 或 'std::basic_ostream> &std::operator (std::basic_ostream> &,const char *)' 2> c:\program files (x86)\microsoft visual studio 14.0\vc\include\ostream(824): 注意: 或 'std::basic_ostream> &std::operator (std::basic_ostream> &,char)' 2> c:\program 文件 (x86)\microsoft visual studio 14.0\vc\include\ostream(950): 注意: 或 'std::basic_ostream> &std::operator (std::basic_ostream> &,const signed char *)' 2> c:\program files (x86)\microsoft visual 工作室 14.0\vc\include\ostream(957):注意:或 'std::basic_ostream> &std::operator (std::basic_ostream> &,signed char)' 2> c:\program files (x86)\microsoft visual studio 14.0\vc\include\ostream(964): 注意: 或 'std::basic_ostream> &std::operator (std::basic_ostream> &,const unsigned char *)' 2> c:\program files (x86)\microsoft visual 工作室 14.0\vc\include\ostream(971):注意:或 'std::basic_ostream> &std::operator (std::basic_ostream> &,unsigned char)' 2> c:\program files (x86)\microsoft visual studio 14.0\vc\include\ostream(981): 注意: 或 'std::basic_ostream> &std::operator (std::basic_ostream> &&,const _Ty &)' 2> 与 2> [ 2> _Ty=mx::Point 2> ] 2> c:\program files (x86)\microsoft visual studio 14.0\vc\include\ostream(1019):注意:或 'std::basic_ostream> &std::operator (std::basic_ostream> &,const std::error_code &)' 2> c:\program files (x86)\microsoft Visual Studio 14.0\vc\include\random(2568):注意:或 'std::basic_ostream> &std::operator (std::basic_ostream> &,const std::bernoulli_distribution &)' 2> c:\program 文件 (x86)\microsoft visual studio 14.0\vc\include\thread(246):注意:或 'std::basic_ostream> &std::operator (std::basic_ostream> &,std::thread::id)' 2> c:(...)\classes\helpertypes.h(87): 注意: 在尝试匹配参数列表时 '(std::ostringstream, const mx::点)' 2> c:(...)\classes(...)\entity\tinyent\tinyent.cpp(1502):注意:见 引用函数模板实例化 'std::string mx::to_string(const T &)' 正在编译 2> 2> [ 2> T=mx::点 2> ]

MinGW 4.7.3:

在 jni/../../Classes/utilities/pattern/pat.h:5:0 包含的文件中, 来自 jni/../../Classes/(...)/entity/tinyent/../ent.h:3, 来自 jni/../../Classes/(...)/entity/tinyent/tinyent.h:2, 来自 jni/../../Classes/(...)/entity/tinyent/tinyent.cpp:1: jni/../../Classes/helpertypes.h:在 'std::string 的实例化中 mx::to_string(const T&) [with T = mx::Point;标准::字符串 = std::basic_string]': jni/../../Classes/(...)/entity/tinyent/tinyent.cpp:1502:20:需要 从这里 jni/../../Classes/helpertypes.h:87:5: 错误:无法绑定 'std::basic_ostream' 左值到 'std::basic_ostream&&' 操作系统 & 的参数 1 std::operator&&, const _Tp&) [与_CharT = char; _Traits = std::char_traits; _Tp = mx::点]' 运算符&& __os, const _Tp& __x) ^

触发实例化的位置:

tinyent.h

#pragma once
#include <string>
#include "ent.h"
#include "mxbasic_utilities.h" // definition of my overload included!

class TinyEnt : public Ent

public:
    // ...
    virtual std::string toString() const override;

private:
    // ...
    int _posX, posY;
;

tinyent.cpp

#include "tinyent.h"

// ...

std::string toString() const

    return std::string("TinyEnt ") + mx::to_string(id()) + " at " // decltype(id()) is ULL, no problem here
        + mx::to_string(mx::Point(_posX, _posY)); // place of instantiation, overload not found

如果我将我的 to_string 函数的定义放在这个 toString 函数的前面,它就可以工作:

备用tinyent.cpp

#include "tinyent.h"


template<typename T>
std::string to_string(const T& val)

    std::ostringstream os;

    os.flags(std::ios::fixed);
    os.precision(2);

    os << val; 

    return os.str();


std::string toString() const

    return std::string("TinyEnt ") + mx::to_string(id()) + " at "
        + ::to_string(mx::Point(_posX, _posY)); // no problems this way

我完全不知道该怎么办。我已经检查了包含 如果可能存在递归包含,则没有层次结构(helpertypes.h 仅取决于 stl 标头)。我尝试从地面 0 重建,并尝试使用不同的编译器。您是否有任何提示可能出了什么问题?

编辑: 在我的 mx::to_string 函数中使用完整的命名空间限定条件时(如:::operator(os, val);),我收到不同的错误消息。当使用 size_t (在这种情况下为 unsigned int )时,实例化会引发歧义错误,就好像在第一轮参数查找中没有找到它一样。

MSVC15 和 MinGW 产生的错误信息。

在 jni/../../Classes/utilities/pattern/pat.h:5:0 包含的文件中, 来自 jni/../../Classes/patmgr.h:4, 来自 jni/../../Classes/patmgr.cpp:1: jni/../../Classes/helpertypes.h: 在 'std::string 的实例化中 mx::to_string(const T&) [with T = unsigned int;标准::字符串 = std::basic_string]': jni/../../Classes/utilities/pattern/pat_bit_spec.h:185:37:必需 从这里 jni/../../Classes/helpertypes.h:94:22: 错误:调用 重载 'operator

在这个部分: 错误:重载 'operator

【问题讨论】:

明确一点:你的 helpertypes.h (仍然)是否包含带有 operator&lt;&lt; 定义的头文件? 另外,您可以尝试将您的行 os &lt;&lt; val 更改为(完全命名空间限定)ms::operator&lt;&lt;(os, val) 以查看问题所在 helpertypes.h 不需要包含重载运算符的定义,这是实例化的工作。完全限定的命名空间将是 ::operator 同意它不需要查看定义,但它确实至少需要查看一个声明,该声明必须出现在编译单元比 helpertypes.h 中的to_string() 函数。尝试在您的 to_string() 声明上方添加行 std::ostream&amp; operator&lt;&lt;(std::ostream&amp; os, const mx::Point&amp; point); 以确认/反驳这一点 我尝试了前向声明,遗憾的是没有解决。有趣的是,如果我使用 ::operator 【参考方案1】:

由于某种原因,编译器认为它不会再考虑我在全局命名空间中定义的重载。然后我尝试在与朋友相同的命名空间中定义重载,但后来我得到了这些歧义错误。引发歧义错误是因为我留下了重载的前向声明,就好像它是在全局命名空间中定义的,而不是在 mx:: 命名空间中。一旦我删除了前向声明并让编译器利用 ADL 的力量,看在上帝的份上,它编译了。

为什么在全局命名空间中查找重载失败(以及为什么只在一个编译单元中)对我来说仍然是一个长期的谜。

故事的寓意: 从现在开始,我只会在类所在的同一个命名空间中声明运算符重载。

【讨论】:

以上是关于在标头之间调用时,模板实例化无法匹配重载流运算符的参数列表的主要内容,如果未能解决你的问题,请参考以下文章

[ C++ ] 类与对象(下)日期类Date补充及流提取和流插入运算符重载

C++ 输入/输出运算符重载

运算符重载

函数申明对函数模板实例化的屏蔽

C++模板

C++模板