2016-03-27 39 views
5

Ho battuto la testa contro questo per un paio di giorni, cercandolo e anche cercando codice simile in progetti open source: non riesco davvero a trovare ciò che sto scorrettamente.Funzione modello amico dichiarata all'interno della classe modello che causa un errore di collegamento simbolico non definito

Essenzialmente, dato il codice di seguito (ricavato dalla sua essenza):

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

quando utilizzato nel codice come questo:

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

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

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

    return EXIT_SUCCESS; 
} 

ottengo il seguente errore di linker:

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) 

Per quanto posso dire, quanto sopra è tutto il codice C++ 11 legale; il modello è ben definito, eppure, sembra in qualche modo sfuggire alla capacità del linker di trovarlo.

Questa è costruito usando CMake su 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}) 

Che cosa dà?

Grazie in anticipo!

Aggiornamento In seguito alla domanda, ho anche eseguito il seguente, stesso risultato:

$ 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

Sì, compila e gli include sono i "soliti sospetti" 'iostream' principalmente; Sto usando cmake - aggiungendolo alla domanda. Grazie, per il collegamento, lo controllerò nel frattempo. – Marco

risposta

6

La dichiarazione all'interno della classe come amico è funzione non-modello, e la definizione di fuori della classe è dima funzione, non corrispondono. E per overload resolution, la funzione non modello verrà selezionata prima della specializzazione delle funzioni del modello, ecco perché si è verificato un errore di collegamento di simboli non definiti.

Si potrebbe modificare la dichiarazione in funzione del modello:

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

LIVE

o definire la funzione all'interno della classe:

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

LIVE

+0

Grazie! Questo era ** esattamente ** it - Avevo appena scoperto il "colpevole" come nella funzione "amico", come la rimozione della dichiarazione all'interno della classe per esso, tutto funzionava. Quello che non avevo capito era che dovevo aggiungere anche il 'template' all'interno della definizione della classe! – Marco

+1

@Marco Sì, è necessario dire che è una funzione modello. La funzione non modello e la funzione modello non sono la stessa cosa. – songyuanyao

1

La definizione dovrebbe corrispondere una amico del modello dichiarato azione, che non hai.

A volte vuoi solo consentire tipi molto specifici Node<T>, quindi questo è come lo faresti.

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

O semplicemente cambiare la vostra definizione e ne fanno un template-amico.

0

ci sono circa tre modi per sovraccaricare operator<< per il vostro modello di classe Node<T>:

  1. Dal momento che si fornisce una funzione di membro publicvalue(), in realtà non c'è bisogno di un friend ma può invece definire un non-amico non modello di funzione -Membro interamente in termini di interfaccia public di Node<T>

Live Example 1:

01.235.164,106 mila
template <typename T> 
std::ostream& operator <<(std::ostream& out, const Node<T>& node) { 
    return out << node.value(); 
} 
  1. Per ogni specializzazione di Node<T>, è possibile utilizzare una funzione non membro che si definisce un in-class (e quelli che vogliono vivere nello spazio dei nomi allegando il modello di classe)

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. Per ogni specializzazione di Node<T>, è possibile definire una specializzazione che accompagna di una fu template nction dichiarando la friend usando l'operator<< <> sintassi (o equivalente operator<< <T> sintassi)

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

C'è anche una quarta possibilità da befriending tutti specializzazioni di un template <typename U> operator<<(std::ostream&, const Node<U>&) al modello di classe Node<T> (la prima opzione della risposta di @songyuanyao) ma secondo me è eccessivo.

Si consiglia di utilizzare l'opzione 1 se è possibile esprimere l'I/O in termini di interfaccia pubblica e l'opzione 2 o 3 in caso contrario. Questi due sono per lo più equivalenti, con alcune piccole differenze nella ricerca del nome e nelle conversioni implicite durante la deduzione degli argomenti.