10

I have the following code in a header only file.

#pragma once

class error_code {
public:
    unsigned __int64 hi;
    unsigned __int64 lo;    
};

std::ostream& operator<< (std::ostream& o, const error_code& e) {
    return o << "[" << e.hi << "," << e.lo << "]";
}

I get linkage error, when there are 2 cpp in the project include this header file.

error LNK2005: "class error_code __cdecl operator|(class error_code const &,class ViTrox::error_code const &)" (??U@@YA?AVerror_code@0@ABV10@0@Z) already defined in xxx.obj

I know I can resolve this problem, if I move the definition of operator<< to a cpp file, or to a DLL file.

However, I just would like to have them in a SINGLE header file. Is there any technique to achieve so? Or must I separate the definition to another file?

Drew Dormann
  • 59,987
  • 13
  • 123
  • 180
Cheok Yan Cheng
  • 47,586
  • 132
  • 466
  • 875

4 Answers4

18

Use the inline keyword.

inline std::ostream& operator<< (std::ostream& o, const error_code& e) {
    return o << "[" << e.hi << "," << e.lo << "]";
}
Logan Capaldo
  • 39,555
  • 5
  • 63
  • 78
  • Out of curiosity, do you know where in the spec it says that this works? I've been using this as a hack for a while, but I've never seen conclusive proof that it works correctly. – templatetypedef Jan 05 '11 at 01:59
  • 2
    Not chapter and verse, but IIRC if you don't have `inline` it violates ODR (and if you do have inline but two or more different bodies it also violates ODR). – Logan Capaldo Jan 05 '11 at 02:00
  • The compiler must see inline and template function bodies. Otherwise it can't inline them or instantiate them at the time of compiling separate translation units. – wilhelmtell Jan 05 '11 at 02:01
  • 3
    `inline` is just a hint to the compiler, it may not inline sometimes. – Nawaz Jan 05 '11 at 02:04
  • 4
    @Nawaz, yes for the purposes of optimization. But in this case we're leveraging it to specify linkage. – Logan Capaldo Jan 05 '11 at 02:06
  • 1
    @Nawaz: No! inline is not just a hint to the compiler. This is a correct and appropriateuse of inline. – CB Bailey Jan 05 '11 at 02:11
  • 1
    @templatetypedef: You shouldn't consider this entirely appropriate use of inline to be a hack. It is guaranteed to work it's what inline is for, see the section on the one definition rule, reference to follow... – CB Bailey Jan 05 '11 at 02:15
  • 1
    @Charles Bailey- Just looked it up - 3.2.3. Thanks for bringing this to my attention! I never knew that this was guaranteed to work. – templatetypedef Jan 05 '11 at 02:20
  • @templatetypedef: thanks, I didn't have a standard to hand. There's lots of previous discussion on inline here as well as other questions. http://stackoverflow.com/questions/1932311/when-to-use-inline-funtion-and-when-not-to-use-it/1932580#1932580 – CB Bailey Jan 05 '11 at 02:23
  • 2
    @Logan Capaldo: inline doesn't affect a function's linkage. inline functions still have external linkage by default. – CB Bailey Jan 05 '11 at 02:43
  • 1
    Can I pretend I used finger quotes when typing that comment? – Logan Capaldo Jan 05 '11 at 02:47
  • 1
    @Logan Capaldo: Sure, why not. :) – CB Bailey Jan 05 '11 at 02:56
7

Either make the function inline:

inline std::ostream& operator<< (std::ostream& o, const error_code& e) {
    return o << "[" << e.hi << "," << e.lo << "]";
}

or make it a template function:

template<class Ch, class Tr>
std::basic_ostream<Ch,Tr>& operator<< (std::basic_ostream<Ch,Tr>& o,
                                       const error_code& e) {
    return o << "[" << e.hi << "," << e.lo << "]";
}
wilhelmtell
  • 57,473
  • 20
  • 96
  • 131
3

You can make the function static. It specifies internal linkage, so the linker won't care if the function is already defined in other translation units.

Or, as already mentioned, you can make it inline. It still has external linkage, but the standard allows external inline functions to have a definition in multiple translation units.

Timo
  • 5,125
  • 3
  • 23
  • 29
0

Define this function in .cpp file (not in .h file)

//yoursource.cpp
#include "yourheader.h"

std::ostream& operator<< (std::ostream& o, const error_code& e) {
    return o << "[" << e.hi << "," << e.lo << "]";
}
Nawaz
  • 353,942
  • 115
  • 666
  • 851