在模板类中声明的朋友模板函数导致未定义的符号链接错误

时间:2016-03-27 02:47:07

标签: c++ templates c++11 operator-overloading friend

我一直在反对这个问题几天,查找它并在开源项目中寻找类似的代码:无法真正找到我做错的事情。

基本上,考虑到下面的代码(提炼到其本质):

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

在以下代码中使用时:

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

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

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

    return EXIT_SUCCESS;
}

我收到以下链接器错误:

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)

据我所知,以上是所有合法的C ++ 11代码;模板是明确定义的,然而,它似乎以某种方式逃脱了链接器找到它的能力。

这是在OS X上使用cmake构建的:

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})

是什么给出了?

提前致谢!

更新关于这个问题,我还运行了以下相同的结果:

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

3 个答案:

答案 0 :(得分:7)

作为朋友在类中的声明是非模板函数,并且类外的定义是模板函数,它们不匹配。对于overload resolution,将在模板函数专门化之前选择非模板函数,这就是未定义符号链接错误发生的原因。

您可以将声明更改为模板功能:

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

LIVE

或在类中定义函数:

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

LIVE

答案 1 :(得分:1)

您的定义将与您没有的模板化朋友声明相匹配。

有时您只想允许非常具体的Node<T>类型,因此您就是这样做的。

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

或者只是更改您的定义并使其成为模板化的朋友。

答案 2 :(得分:0)

大约有三种方法可以为您的班级模板operator<<重载Node<T>

  1. 由于您提供了public成员函数value(),因此您实际上并不需要friend,而是可以完全按照public来定义非朋友非成员函数模板。 Node<T>
  2. template <typename T> std::ostream& operator <<(std::ostream& out, const Node<T>& node) { return out << node.value(); } 界面

    Live Example 1

    Node<T>
    1. 对于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_; } }; 的每个特化,您可以使用定义类内的非成员函数(并且它将存在于包含类模板的命名空间中)
    2. Live Example 2

      Node<T>
      1. 对于friend的每个专业化,您可以通过使用operator<< <>语法(或等效的operator<< <T>语法)声明// 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_; } 来定义函数模板的附带专门化
      2. Live Example 3

        template <typename U> operator<<(std::ostream&, const Node<U>&)

        第四种可能性是通过将Node<T>的所有特化与朋友分享current(@songyuanyao的答案的第一个选项),但在我看来这是过度的。

        如果您可以根据公共界面表达I / O,我建议使用选项1,否则使用选项2或3。这两个大部分是等价的,在参数推断期间名称查找和隐式转换之间存在一些细微差别。