2

In C++, I am attempting to write a templated version of operator<< for std::set so that I don't have to write one for each permutation of contained value types.

I have defined my header as utils.h:

#ifndef UTILS
#define UTILS

#include <ostream>
#include <set>

template <typename T>
std::ostream &operator<<(std::ostream &os, const std::set<T> &tgt);

template <typename T>
inline std::ostream &operator<<(std::ostream &os,
                                const std::set<T> *tgt) {
  return os << *tgt;
}

#endif 

The implementation is in utils.cpp:

#include "utils.h"

template <typename T>
std::ostream &operator<<(std::ostream &os, const std::set<T> &tgt) {
  os << "{";
  bool first = true;
  for (typename std::set<T>::const_iterator i = tgt.begin(); i != tgt.end();
       i++) {
    if (!first) {
      os << ", ";
    } else {
      first = false;
    }
    os << i;
  }
  os << "}";
  return os;
}

My attempt to use it in cout.cpp:

#include <iostream>
#include <set>
#include "utils.h"

class Value {
public:
  const int value;
  Value(const int value) : value(value) {}
};

std::ostream &operator<<(std::ostream &os, const Value &tgt) {
  os << "Value(" << tgt.value << ")";
  return os;
}

inline std::ostream &operator<<(std::ostream &os, const Value *tgt) {
  return os << *tgt;
}

int main(const int argc, const char **argv) {
  std::cout << argc << std::endl;
  std::cout << *argv << std::endl;
  Value v(10);
  std::cout << v << std::endl;
  Value *w = &v;
  std::cout << *w << std::endl;
  std::set<const Value *> vs;
  vs.insert(w);
  vs.insert(&v);
  Value x = Value(12);
  vs.insert(&x);
  std::cout << vs << std::endl;
  std::set<const Value *> *p = &vs;
  std::cout << p << std::endl;
  return 0;
}

I am attempting to compile & run it with:

clang++ -c utils.cpp
clang++ -o cout cout.cpp utils.o
./cout

The error that clang++ produces is:

tmp/cout-6e3988.o: In function `main':
cout.cpp:(.text+0x24d): undefined reference to `std::ostream& operator<< <Value const*>(std::ostream&, std::set<Value const*, std::less<Value const*>, std::allocator<Value const*> > const&)'
/tmp/cout-6e3988.o: In function `std::ostream& operator<< <Value const*>(std::ostream&, std::set<Value const*, std::less<Value const*>, std::allocator<Value const*> > const*)':
cout.cpp:(.text._ZlsIPK5ValueERSoS3_PKSt3setIT_St4lessIS5_ESaIS5_EE[_ZlsIPK5ValueERSoS3_PKSt3setIT_St4lessIS5_ESaIS5_EE]+0x19): undefined reference to `std::ostream& operator<< <Value const*>(std::ostream&, std::set<Value const*, std::less<Value const*>, std::allocator<Value const*> > const&)'
clang: error: linker command failed with exit code 1 (use -v to see invocation)

I understand that it's not finding the appropriate operator<< implementation during linking, but I have no idea how to go about resolving this issue.

Thanks.

oconnor0
  • 3,154
  • 6
  • 34
  • 52
  • You might want to have a look at [this](http://kuhllib.com/2012/01/22/source-organization-of-templates/). – Dietmar Kühl Jul 26 '13 at 22:51
  • 1
    Although it is annoying that the standard C++ containers don't have output operators, defining your own isn't a good idea: if you do it, others might do it as well and you'd end up with violations of the one definition rule. Also, if you define an operator working on any of the standard containers, you'd need to define it in namespace `std` which you are only allowed it if it involves something you have defined. – Dietmar Kühl Jul 26 '13 at 22:54
  • 1
    `os << i;` are you trying to output the iterator? :) – Karoly Horvath Jul 26 '13 at 22:56
  • @JesseGood: Of course, there are other portable ways to implement templates than putting them into headers! In fact, the above approach does work if the template gets explicitly instantiated for all types it is used with in utils.cpp (assuming, the actual implementation is correct which it seems not to be based on Karoly Horvath's comment). – Dietmar Kühl Jul 26 '13 at 22:56
  • @DietmarKühl: Yes, that is explained in the answer to the linked question along with other solutions. – Jesse Good Jul 26 '13 at 22:58
  • @DietmarKühl, why couldn't I define those operators in my own namespace so that only members of my project and thus using that namespace would be able to output them? This should avoid having more than one definition. – oconnor0 Jul 26 '13 at 22:58
  • @oconnor0: The operators won't be found in all interesting cases (e.g. when using them from template instantiations). You can define the operators in your namespace in which case only instantiations involving other types from your namespace would find them. When mixing the declaration with other declarations from other libraries it can still yield problems, e.g., because the choice of operator to be used becomes ambiguous. – Dietmar Kühl Jul 26 '13 at 23:05

2 Answers2

1

You should define templates in the header files where you declare them so that the compiler and the linker can see the definitions.

Kristian Duske
  • 1,769
  • 9
  • 14
0

Try making it const Value* const in your declaration...