2016-03-27 17 views
5

Od kilku dni walę w to z głową, szukając go i szukając podobnego kodu w projektach open source: nie mogę tak naprawdę znaleźć robię niepoprawnie.Funkcja szablonu znajomego zadeklarowana w klasie szablonu powodująca niezdefiniowany błąd łącza symbolu

Zasadniczo, podane poniżej kod (destylowana do swej istocie):

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

gdy używany w kodzie takich jak to:

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

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

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

    return EXIT_SUCCESS; 
} 

pojawia się następujący błąd linkera:

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) 

O ile mi wiadomo, powyższy kod jest legalnym kodem C++ 11; szablon jest dobrze zdefiniowany, a mimo to wydaje się, że w jakiś sposób nie ma możliwości znalezienia go przez linker.

ten jest zbudowany przy użyciu cmake na 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}) 

Co daje?

Z góry dziękuję!

aktualizacji Po pytaniu, jakie prowadził również, sam rezultat:

$ 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

Tak, kompiluje i obejmuje głównie "zwykłych podejrzanych" iostream; Używam cmake - dodając go do pytania. Dzięki za link, sprawdzę to w międzyczasie. – Marco

Odpowiedz

6

Deklaracja wewnątrz klasy jako przyjaciel jest funkcja bez szablonu i definicja poza klasa jest szablon funkcja, nie pasują. I dla overload resolution, funkcja nie-szablonowa zostanie wybrana przed specjalizacją funkcji szablonu, dlatego wystąpił błąd łącza niezdefiniowanych symboli.

Można zmienić deklarację jako funkcję szablonu:

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

LIVE

lub określenia funkcji wewnątrz klasy:

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

LIVE

+0

Dzięki! To było ** dokładnie ** to - właśnie odkryłem "winowajcę", jak w funkcji "przyjaciel", jako usunięcie deklaracji wewnątrz klasy, wszystko działało. Nie zdawałem sobie sprawy, że muszę dodać "szablon" również w definicji klasy! – Marco

+1

@Marco Tak, musisz powiedzieć, że jest to funkcja szablonu. Funkcja bez szablonu i funkcja szablonu nie są takie same. – songyuanyao

1

Twoja definicja by dopasować szablonowa deklaracja przyjaciela acji, której nie masz.

Czasami chcesz zezwolić na bardzo specyficzne typy Node<T>, więc tak możesz to zrobić.

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

Albo po prostu zmienić definicję i sprawiają, że na matrycy przyjaciela.

0

Istnieje około trzy sposoby przeciążenia operator<< dla klasy szablonu Node<T>:

  1. Od podać funkcję public członek value(), to w rzeczywistości nie potrzebują friend ale może zamiast określenia non-non znajomego -Członek szablon funkcja całkowicie pod względem interfejsu public z Node<T>

Live Example 1:

template <typename T> 
std::ostream& operator <<(std::ostream& out, const Node<T>& node) { 
    return out << node.value(); 
} 
  1. Dla każdej specjalizacji Node<T>, można użyć funkcji non-member, że zdefiniowanie w klasie (i które będą żyć w przestrzeni nazw załączając szablonu klasy)

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. Dla każdego specjalizacji Node<T> można określić towarzyszący specjalizacji fu nction szablon przez deklarowania friend używając składni operator<< <> (lub równowartość składni operator<< <T>)

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

Jest też czwarta możliwość przez zaprzyjaźnienie wszystkie specjalizacje w template <typename U> operator<<(std::ostream&, const Node<U>&) do klasy szablonu Node<T> (the pierwsza opcja odpowiedzi @songyuanyao), ale moim zdaniem to przesada.

Polecam korzystanie z opcji 1, jeśli można wyrazić operacje we/wy pod względem interfejsu publicznego, a opcja 2 lub 3 w przeciwnym razie. Te dwa są w większości równoważne, z niewielkimi różnicami w wyszukiwaniu nazw i niejawnych konwersjach podczas odliczania argumentów.