Reputation: 6018
As has been answered before, namespacing operator overloads is considered good practice, so that's what I want to do.
Problem: It only compiles if I don't. Am I just doing it wrong, or have I found an exception where it is not possible?
Here is a single translation unit for easy repro:
// lib/halfseconds.h:
#include <chrono>
namespace lib {
using halfseconds = std::chrono::duration<intmax_t, std::ratio<1, 2> >;
}
// lib/debug.h:
#include <ostream>
namespace lib {
std::ostream& operator<<(std::ostream& o, lib::halfseconds halves)
{
double seconds = halves.count();
seconds /= lib::halfseconds::period::den;
o << seconds << 's';
return o;
}
}
// demo/main.cpp:
#include <iostream>
int main()
{
lib::halfseconds threeHalvseconds(3);
std::cout << threeHalvseconds << '\n'; // 1.5s
}
What does the compiler say? G++ version 8.2.1 says «no match for operator<<» and spews one daunting list (208 lines) of candidates. I suppose none of those are relevant, as I wouldn't get this error if the relevant one wasn't missing.
Upvotes: 0
Views: 220
Reputation: 10740
The principle you're trying to use is called Argument Dependent Lookup. If I have a function and a type declared in the same namespace, I can use them together outside of the namespace without having to specify which namespace the function is from:
//In MyClass.h
namespace foo {
class MyClass { /* stuff */ };
}
//In doStuff.h
namespace foo {
void doStuff(MyClass c) { /* stuff */ }
}
//in main.cc
int main() {
foo::MyClass tom; //I'm bad with names
doStuff(tom); //Here, we don't have to specify the namespace
}
This is almost what's happening in your example. The difference is that halfseconds
isn't actually declared in namespace lib
. Because halfseconds
is an alias, it's actually declared in std::chrono
, and when you put the operator<<
overload in namespace lib
, the compiler doesn't check for it.
The simplest way to fix this is to declare a new type in namespace lib:
// lib/halfseconds.h:
#include <chrono>
namespace lib {
class halfseconds
: public std::chrono::duration<intmax_t, std::ratio<1, 2>>
{
public:
using Base = std::chrono::duration<intmax_t, std::ratio<1, 2>>;
using Base::Base; //Use the constructor
};
}
// lib/debug.h:
#include <ostream>
namespace lib {
std::ostream& operator<<(std::ostream& o, lib::halfseconds halves)
{
double seconds = halves.count();
seconds /= lib::halfseconds::period::den;
o << seconds << 's';
return o;
}
}
// demo/main.cpp:
#include <iostream>
int main()
{
lib::halfseconds threeHalvseconds(3);
std::cout << threeHalvseconds << '\n'; // 1.5s
}
You can use it anywhere you'd use std::chrono::duration
, it has all the same functionality, and because it's defined in the lib
namespace it can be used with any other functions in the lib
namespace without having to prefix lib
!
Upvotes: 3
Reputation: 332
The problem here is that once you have put operator<< in a namespace, you should explicitly tell the compiler you would like to use the namespace, otherwise it's hidden.
Either:
Upvotes: 3