I have been banging my head against this for a couple of days, looking it up and also looking for similar code in open source projects: can't really find what I'm doing incorrectly.
Essentially, given the code below (distilled to its essence):
#include <iostream>
using std::cout;
using std::endl;
using std::string;
template <typename T>
class Node {
T value_;
public:
Node(const T& value) : value_(value) {}
T const value() const { return value_; }
friend
std::ostream& operator <<(std::ostream& out, const Node<T>& node);
Node<T> operator +(const Node<T>& other) {
return Node(value() + other.value());
}
};
template <typename T>
std::ostream& operator <<(std::ostream& out, const Node<T>& node) {
return out << node.value();
}
when used in code such as this:
int main(int argc, char* argv[]) {
Node<string> node("node X");
cout << node << endl;
Node<int> three(3);
cout << three << endl;
return EXIT_SUCCESS;
}
I get the following linker error:
Undefined symbols for architecture x86_64:
"operator<<(std::__1::basic_ostream<char, std::__1::char_traits<char> >&, Node<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > const&)", referenced from:
_main in StlPractice.cpp.o
"operator<<(std::__1::basic_ostream<char, std::__1::char_traits<char> >&, Node<int> const&)", referenced from:
_main in StlPractice.cpp.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
As far as I can tell, the above is all legal C++11 code; the template is well-defined, and yet, it seems to somehow escape the ability of the linker to find it.
This is built using cmake on OS X:
cmake_minimum_required(VERSION 3.3)
project(simple_template)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
set(SOURCE_FILES src/simple_template.cpp)
add_executable(simple ${SOURCE_FILES})
What gives?
Thanks in advance!
Update Following the question, I've also ran the following, same result:
$ clang++ src/simple_template.cpp
Undefined symbols for architecture x86_64:
"operator<<(std::__1::basic_ostream<char, std::__1::char_traits<char> >&, Node<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > const&)", referenced from:
_main in StlPractice-e20370.o
"operator<<(std::__1::basic_ostream<char, std::__1::char_traits<char> >&, Node<int> const&)", referenced from:
_main in StlPractice-e20370.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
The declaration inside the class as friend is non-template function, and the definition outside the class is template function, they don't match. And for overload resolution, non-template function will be selected prior to template function specialization, that's why undefined symbols link error occured.
You could change the declaration as template function:
template<typename X>
friend std::ostream& operator <<(std::ostream& out, const Node<X>& node);
LIVE
or define the function inside the class:
friend
std::ostream& operator <<(std::ostream& out, const Node<T>& node) { return out << node.value(); }
LIVE
Your definition would match a templated friend declaration, which you don't have.
Sometimes you only want to allow very specific Node<T>
types, so this is how you would do it.
http://rextester.com/GZKCJQ35441
template <typename T>
class Node {
T value_;
public:
Node(const T& value) : value_(value) {}
T const value() const { return value_; }
friend
std::ostream& operator <<(std::ostream& out, const Node<T>& node);
Node<T> operator +(const Node<T>& other) {
return Node(value() + other.value());
}
};
std::ostream& operator <<(std::ostream& out, const Node<int>& node) { return out << node.value_; }
std::ostream& operator <<(std::ostream& out, const Node<string>& node) { return out << node.value_; }
Or simply change your definition and make it a templated-friend.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With