<<运算符覆盖用g ++编译而不是windows

时间:2018-05-03 02:10:26

标签: c++ c++11 visual-c++ operator-overloading name-lookup

我正在尝试将应用程序移植到win-dows(具有讽刺意味的是,我知道)。以下赤裸裸的例子说明了这个问题。使用VS12和VS14进行编译时出现以下错误:

C2679 binary '<<': no operator found which takes a right-hand operand
of type 'std::chrono::time_point<std::chrono::system_clock,std::chrono::system_clock::duration>'
(or there is no acceptable conversion)

使用g ++在Ubuntu上没有错误。我错过了什么?

logger.h

#pragma once
#include "stdafx.h"

#include <chrono>
#include <ostream>

std::ostream& operator<<(std::ostream &out, const std::chrono::time_point<std::chrono::system_clock> &time_point);

namespace Logging
{
    inline std::chrono::time_point<std::chrono::system_clock> timestamp()
    {
        std::chrono::time_point<std::chrono::system_clock> time_point = std::chrono::system_clock::now();
        return time_point;
    }

    class Event
    {
    public:
        Event() : time_point(timestamp())
        {
        }
        typedef std::unique_ptr< Event > Ptr;

        friend std::ostream& operator<<(std::ostream &out, const Ptr &p)
        {
            out << p->time_point << "\t";  // << LINE CAUSING ERROR
            return out;
        }

    private:
        const std::chrono::time_point<std::chrono::system_clock> time_point;
    };
}

logger.cpp

#include "stdafx.h"

#include <memory>
#include <ctime>
#include "logger.h"


std::ostream& operator<<(std::ostream &out, const std::chrono::time_point<std::chrono::system_clock> &time_point)
{
    std::time_t time = std::chrono::system_clock::to_time_t(time_point);
    struct tm t;
    localtime_s(&t, &time);  //localtime(&time) on linux
    char buf[30];
    int ret = ::strftime(buf, 30, "%Y/%m/%d %T", &t);
    out << buf;
    return out;
}

命令和标志

linux:

g++ -std=c++11 -Wall logger.cpp app.cpp -o app

windows:

/Yu"stdafx.h" /GS /analyze- /W3 /Zc:wchar_t /ZI /Gm /Od /sdl /Fd"Debug\vc140.pdb" /Zc:inline /fp:precise /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_UNICODE" /D "UNICODE" /errorReport:prompt /WX- /Zc:forScope /RTC1 /Gd /Oy- /MDd /Fa"Debug\" /EHsc /nologo /Fo"Debug\" /Fp"Debug\chron_test.pch" 

1 个答案:

答案 0 :(得分:3)

简短回答:这是MSVC 2015及更早版本中的编译器错误;要解决它,请在给出错误的行之前写using ::operator<<;

此问题涉及内联好友定义的名称查找。 以下是该问题的简化示例:

namespace R { struct S {}; }
void f(R::S) {}

namespace N
{
    struct E
    {
        R::S var;
        friend void f(E e) { f(e.var); }   // OK, should find f(R::S)
    };
}

通常,非限定名称查找会做两件事:

  • 在当前范围中查找名称。如果找不到,请查看父作用域等,直到并包括全局名称空间。 找到名称后停止。也就是说,如果名称在当前作用域中并且也在父作用域中,则不会找到父作用域中的名称。
  • ADL,即也搜索函数调用的任何参数的名称空间。

请注意,在此代码中f(R::S)未在命名空间R中声明,因此ADL永远找不到它。它只能通过不等式查找的第一部分找到。

因此,f内发生的任何名称namespace N都可能隐藏全局f,这是一个潜在的问题。如果您移除friend行并将void f(E e) { f(e.var); }作为N中的函数放入E,则可以看到此操作。然后名称查找找到N::f,并停止搜索,永远不会找到::f

现在,在类中首先定义<{1}}函数的名称查找有点不寻常。引自cppreference

  

在类或类模板X中的友元声明中首先声明的名称成为X的最内层封闭命名空间的成员,但是对于查找是不可见的(除了与X相关的依赖于参数的查找),除非在提供了命名空间范围。

这意味着在调用friend中,函数f(e.var)实际上对查找不可见。因此,搜索应该继续增加范围,直到找到N::f,然后成功。

MSVC 2015似乎确实知道这个朋友查找规则,因为它正确拒绝::f,但是它无法继续查找外部范围以获取另一个名称声明。

struct A { friend void a() { a(); } };声明表示搜索using ::operator<<;会找到N;看到MSVC 2015接受了这一点,显然它仍在搜索::operator<<但是如果搜索失败则不会向上递归。

评论:如果ADL找不到N,那么函数名称被遮蔽的问题始终是个问题。即使在正确的编译器中,当您遇到一些不相关的operator<<干扰时,您可能会发现代码会烦恼地停止工作。例如,如果您在operator<<中定义operator<<,那么即使在g ++和MSVC 2017中,代码也会停止工作。

在这种情况下,相同的namespace Logging解决方法可行,但是必须继续这样做很烦人。你真的必须在任何名称空间using ::operator<<内部using ::operator<<声明它自己的任何类型的N,这仍然很烦人,但不那么令人讨厌。为此目的,最好使用具有某种独特名称的函数,而不是operator<<

请注意,ADL无法找到operator<<,因为所有参数都在operator<<(std::ostream, std::chrono...)中,但std不在::operator<<中。将您自己的免费函数添加到std是未定义的行为,因此您无法以这种方式解决它。