Friend template function declared inside template class causing undefined character error

I hit my head about it a couple of times, looking through it and looking for similar code in open source projects: I can’t find what I'm doing wrong.

Essentially, given the code below (inherently overridden):

#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, for example:

 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, this is all legal C ++ 11 code; the template is well defined, and yet it seems to somehow avoid the linker being able 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 this question, I also performed 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) 
+5
source share
3 answers

Declaring inside a class as a friend is a function without a template, and defining outside a class is a function of a template, they do not match. And to resolve the overload , the template selection function will be selected before the template function specializes, therefore, an error occurred while communicating undefined characters.

You can change the declaration as a function of the template:

 template<typename X> friend std::ostream& operator <<(std::ostream& out, const Node<X>& node); 

Live

or define a function inside the class:

 friend std::ostream& operator <<(std::ostream& out, const Node<T>& node) { return out << node.value(); } 

Live

+6
source

Your definition will correspond to a friend’s template declaration that you don’t have.

Sometimes you only need to allow very specific types of Node<T> , so here is how you 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 just change your definition and make it a template friend.

+1
source

There are approximately three ways to overload operator<< for your Node<T> class template:

  • Since you provide a member function with public value() , you really don't need friend , but instead you can define a non-friend function template that is not a member, in terms of the public Node<T> interface

Live 1 example :

 template <typename T> std::ostream& operator <<(std::ostream& out, const Node<T>& node) { return out << node.value(); } 
  1. For each Node<T> specialization, you can use a non-member function that you define in the class (and which will live in a namespace that spans the template of your class).

Live 2 example :

 template <typename T> class Node { // generates a non-template operator<< for this T friend std::ostream& operator<<(std::ostream& out, const Node<T>& node) { return out << node.value_; } }; 
  1. For each Node<T> specialization, you can define a concurrent specialization of the function template by declaring friend using operator<< <> syntax (or the equivalent operator<< <T> syntax)

Live 3 example

 // forward declare to make function declaration possible template <typename T> class Node; // declaration template <typename T> std::ostream& operator <<(std::ostream& out, const Node<T>& node); template <typename T> class Node { // refers to a full specialization for this particular T friend std::ostream& operator<< <>(std::ostream& out, const Node<T>& node); // note: this relies on template argument deduction in declarations // can also specify the template argument with operator<< <T>" }; // definition template <typename T> std::ostream& operator <<(std::ostream& out, const Node<T>& node) { return out << node.value_; } 

There is also a fourth possibility, supporting all the specializations of the template template <typename U> operator<<(std::ostream&, const Node<U>&) template Node<T> (the first answer from @songyuanyao), but, in my opinion, it's too much.

I would recommend using option 1 if you can express I / O in terms of an open interface, and option 2 or 3 otherwise. The two are mostly equivalent, with some slight differences in name lookups and implicit conversions during argument output.

0
source

Source: https://habr.com/ru/post/1245840/


All Articles