Reputation: 1465
I am trying to port an application to win-dows (ironic, I know). The following bare-bone example illustrates the problem. I get the following error when compiling with VS12 and 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)
No errors on Ubuntu with g++. What am I missing?
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;
}
commands and flags
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"
Upvotes: 3
Views: 89
Reputation: 141638
Short answer: It is a compiler bug in MSVC 2015 and older; to work around it, write using ::operator<<;
just before the line giving the error.
This issue concerns name lookup of inline friend definitions. Here is a simplified example of the problem:
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)
};
}
Generally, unqualified name lookup will do two things:
Note that in this code f(R::S)
is not declared in namespace R
, so it is never found by ADL. It can only be found by the first part of unqualfiied lookup.
So there is a potential problem that any name f
occurring inside namespace N
may hide the global f
. You can see this in action if you remove the friend
line and put void f(E e) { f(e.var); }
as a function in N
(not in E
). Then the name lookup finds N::f
, and stops searching, never finding ::f
.
Now, name lookup of a friend
function first defined inside a class is a bit unusual. Quoting from cppreference:
A name first declared in a friend declaration within class or class template X becomes a member of the innermost enclosing namespace of X, but is not visible for lookup (except argument-dependent lookup that considers X) unless a matching declaration at the namespace scope is provided.
That means that in the call f(e.var)
, the function N::f
is actually not visible for lookup. So the search should keep going up scopes until we find ::f
, and succeed.
MSVC 2015 does seem to know about this friend lookup rule, as it correctly rejects struct A { friend void a() { a(); } };
, however it then fails to continue looking up the outer scopes for another declaration of the name.
The using ::operator<<;
declaration means that the search of N
will find ::operator<<
; seeing as MSVC 2015 accepts this, apparently it is still searching N
but just not recursing upwards if that search fails.
Commentary: This problem with function names being shadowed is always a problem when you have operator<<
which is not found by ADL. Even in a correct compiler, you may find your code annoyingly stops working when you have some unrelated operator<<
interfering. For example if you define an operator<<
in namespace Logging
then , even in g++ and MSVC 2017, the code will stop working.
The same using ::operator<<
workaround works in this case, but it's annoying to have to keep doing that. You really have to do using ::operator<<
inside any namespace N
that declares its own operator<<
of any sort, which is still annoying but a bit less so. It may be preferable to use a function with a somewhat unique name, instead of operator<<
, for this purpose.
Note that it is not possible for operator<<(std::ostream, std::chrono...)
to be found by ADL, because all of the arguments are in std
but ::operator<<
is not in std
. It is undefined behaviour to add your own free functions to namespace std
, so you can't work around it that way either.
Upvotes: 3