2016-03-27 5 views
5

Ich habe meinen Kopf gegen diese für ein paar Tage, nachschlagen und auch auf der Suche nach ähnlichen Code in Open-Source-Projekte: kann nicht wirklich was ich finden mache ich falsch.Friend Vorlage Funktion innerhalb Template-Klasse verursacht verursacht undefined Symbol Link Fehler

, im Wesentlichen der Code unten (auf seine Essenz destilliert):

#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(); 
} 

wenn in Code wie benutzten:

int main(int argc, char* argv[]) { 

    Node<string> node("node X"); 
    cout << node << endl; 

    Node<int> three(3); 
    cout << three << endl; 

    return EXIT_SUCCESS; 
} 

bekomme ich folgende Linkerfehler:

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) 

Soweit ich feststellen kann, ist das oben genannte alles legale C++ 11 Code; Die Vorlage ist wohldefiniert, und doch scheint sie irgendwie der Fähigkeit des Linkers zu entgehen, sie zu finden.

Dieses gebaut wird mit cmake auf 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}) 

Was soll das?

Vielen Dank im Voraus!

aktualisieren die Frage Nach habe ich lief auch die folgenden, das gleiche Resultat:

$ 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) 
+0

Ja, es kompiliert und die Includes sind die "üblichen Verdächtigen" "Iostream" meist; Ich benutze cmake und füge es der Frage hinzu. Danke, für den Link, ich werde es in der Zwischenzeit überprüfen. – Marco

Antwort

6

Die Erklärung innerhalb der Klasse als Freund Nicht-Template-Funktion und die Definition außerhalb der Klasse ist, Vorlage Funktion, sie stimmen nicht überein. Und für overload resolution, Nicht-Template-Funktion wird vor Template-Funktion Spezialisierung ausgewählt werden, deshalb ist undefined Symbole Link Fehler aufgetreten.

Sie die Deklaration als Template-Funktion ändern könnte:

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

LIVE

oder definieren Sie die Funktion innerhalb der Klasse:

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

LIVE

+0

Danke! Das war ** genau ** es - ich hatte gerade den "Schuldigen" wie in der "Freund" -Funktion entdeckt, als die Deklaration innerhalb der Klasse dafür zu entfernen, es hat alles geklappt. Was ich nicht realisiert hatte, war, dass ich die "Vorlage" innerhalb der Klassendefinition hinzufügen musste! – Marco

+1

@Marco Ja, Sie müssen sagen, dass es eine Vorlagenfunktion ist. Nicht-Template-Funktion und Template-Funktion ist nicht das Gleiche. – songyuanyao

1

Ihre Definition ein würde passen Templated Freund deklarieren ation, die Sie nicht haben.

Manchmal möchten Sie nur sehr spezifische Node<T> Typen zulassen, also so würden Sie es tun.

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_; } 

Oder einfach Ihre Definition ändern und es eine Templat-Freundin machen.

0

Es gibt ungefähr drei Möglichkeiten operator<< für Ihre Klassenvorlage, um eine Überlastung Node<T>:

  1. Da Sie bieten eine public Memberfunktion value(), brauchen Sie nicht wirklich brauchen eine friend sondern kann stattdessen eine Nicht-Freund nicht definieren -Mitglied Funktionsvorlage vollständig in Bezug auf die public Schnittstelle von Node<T>

Live Example 1:

template <typename T> 
std::ostream& operator <<(std::ostream& out, const Node<T>& node) { 
    return out << node.value(); 
} 
  1. Für jede Spezialisierung von Node<T> Sie eine nicht-Member-Funktion verwenden können, die Ihnen eine Klasse definieren (und das im Namespace leben Ihre Klassenvorlage umschließenden)

Live Example 2:

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. Für jede Spezialisierung von Node<T>, können Sie eine Begleit Spezialisierung eines fu definieren nction Matrize durch die friend Deklarieren der operator<< <> Syntax (oder die äquivalente operator<< <T> Syntax)

Live Example 3

// 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_; 
} 

Es gibt auch eine vierte Möglichkeit, indem alle Spezialisierungen ein template <typename U> operator<<(std::ostream&, const Node<U>&) Klasse Vorlage befriending Node<T> (die erste Option der Antwort von @songyuanyao), aber meiner Meinung nach ist das Overkill.

Ich würde empfehlen, Option 1 zu verwenden, wenn Sie E/A in Bezug auf die öffentliche Schnittstelle ausdrücken können, und Option 2 oder 3 andernfalls. Diese beiden sind meistens äquivalent, mit einigen geringfügigen Unterschieden in der Namenssuche und impliziten Konvertierungen während der Argumentableitung.

Verwandte Themen