【问题标题】:Friend template overloaded operator <<: unresolved external symbol友元模板重载运算符<<:未解析的外部符号
【发布时间】:2016-06-19 14:12:26
【问题描述】:

我遇到了错误问题

错误 LNK2019 未解析的外部符号 "class std::basic_ostream > & __cdecl cop4530::operator &,class rob::Stack const &)" (??6rob@@YAAAV?$ basic_ostream@DU?$char_traits@D@std@@@std@@AAV12@ABV?$Stack@H@0@@Z) 在函数 _main Project7 c:\Users\Robrik\documents\visual studio 2015\Projects\ 中引用Project7\Project7\post.obj 1

现在,post 所做的只是调用operator&lt;&lt;

声明

namespace rob {     

template < typename T> class Stack {
    friend std::ostream& operator<< (std::ostream& os, const Stack<T>& a);
    void print(std::ostream& os, char ofc = ' ') const;
private:
    std::vector<T> arr;
};

定义

template < typename T>
inline std::ostream & rob::operator<<(std::ostream & os, const Stack<T>& a)                {     
    return a.print(os, ' ');
}
template<typename T>
inline void rob::Stack<T>::print(std::ostream & os, char c) const
{
    for (int i = 0; i != arr.size(); i++)
    {
        os << c << arr[i];
    }
    os << '\n';
}

它们分别位于.h 文件和.hpp 中,我要求运算符不是成员函数(用于赋值)。

【问题讨论】:

  • hpp 文件可能不包含在使用operator&lt;&lt; 的任何文件中。要获得更好的答案,请发布MCVE。显然你没有发布你的真实代码(例如你的Stack 没有任何成员arr)所以你真的是在让我们猜测你在真实代码中可能犯了什么错误。
  • 还要检查template friends FAQ,以防您的问题在那里得到解决
  • 文件被正确地包含在一起,因为在 .h 文件中声明并在 .hpp 文件中定义的其他函数可以工作。只有运算符重载存在链接器问题,我敢肯定,如果我能修复一个,其他的将以相同的方式修复。
  • 我知道您已经解决了这个问题,但请参阅我对错误开始原因的回答以及您可能的替代方案。
  • 我找不到一个像样的副本(尤其是与未解析的符号有关),所以 +1 询问它。

标签: c++ templates operator-overloading friend


【解决方案1】:

代码示例的问题;

template <typename T>
class Stack {
    friend std::ostream& operator<< (std::ostream& os, const Stack<T>& a);
    void print(std::ostream& os, char ofc = ' ') const;
    // ...
};

operator&lt;&lt; 是否被声明为非模板函数。对于与Stack 一起使用的每种类型T,都需要有一个非模板operator&lt;&lt;。例如,如果声明了一个类型Stack&lt;int&gt;,那么一定有一个操作符实现如下;

std::ostream& operator<< (std::ostream& os, const Stack<int>& a) {/*...*/}

由于没有实现,所以链接器找不到它,导致你得到的错误。

作为旁注; gcc 对此发出如下警告

warning: friend declaration 'std::ostream&amp; operator&lt;&lt;(...)' declares a non-template function [-Wnon-template-friend]

note: (if this is not what you intended, make sure the function template has already been declared and add &lt;&gt; after the function name here)

这可能不是预期的,每个实例都有自己的实现。

要纠正这个问题,您可以在Stack 类型之前声明一个模板运算符,然后将其声明为友元,即实例化。语法看起来有点别扭,但看起来如下;

// forward declare the Stack
template <typename>
class Stack;

// forward declare the operator <<
template <typename T>
std::ostream& operator<<(std::ostream&, const Stack<T>&);

template <typename T>
class Stack {
    friend std::ostream& operator<< <>(std::ostream& os, const Stack<T>& a);
    // note the required <>        ^^^^
    void print(std::ostream& os, char ofc = ' ') const;
    // ...
};

template <typename T>
std::ostream& operator<<(std::ostream&, const Stack<T>&)
{
  // ... implement the operator
}

以上代码将操作符的友谊限制为Stack的对应实例化,即operator&lt;&lt; &lt;int&gt;实例化仅限于访问Stack&lt;int&gt;实例化的私有成员。

替代方案包括允许将友谊扩展到模板的所有实例化;

template <typename T>
class Stack {
    template <typename T1>
    friend std::ostream& operator<<(std::ostream& os, const Stack<T1>& a);
    // ...
};

operator&lt;&lt; 的实现可以在类定义内部或外部内联完成。

【讨论】:

  • 对我来说最晦涩难懂的代码,尤其是 'operator(...)' 部分。测试了所有变体,它工作正常,这才是最重要的。我仍然想了解为什么以及如何。好吧,有一天我可能会。
  • 有关 Stroustrup 的 'operator(...)' 示例,请参见 C++ prog lang。 ISBN9780321563842,“模板”一章,par。 23.4.7 第 682 页上的朋友。
【解决方案2】:

您还应该在它实际所属的 rob 命名空间中声明函数签名:

namespace rob {
template <typename T>
class Stack {
    friend std::ostream& operator<< (std::ostream& os, const Stack<T>& a);
};

template < typename T>
std::ostream& operator<< (std::ostream& os, const Stack<T>& a){
    ...
}

【讨论】:

  • 这并没有解决问题,但无论如何,它们应该位于不同的文件中(.h 用于声明,hpp 用于定义)。
  • @Robert Hook 函数头和函数实现仍然在不同的文件中。 “朋友”签名只是此功能访问私有或受保护成员/方法的权限。有了这种访问权限,应该存在一个函数本身,它可以定义为对:头文件中的签名和 cpp 文件中的实现。
  • @Robert BTW,内联和模板都不能在课堂外工作。您应该在标题中放置这种功能。
  • 我让它与模板 朋友 std::ostream& operator& a);在 h 和 template std::ostream& rob::operator& a) 在 hpp 上。谢谢!模板 让我不知所措。
【解决方案3】:

除了@LibertyPaul 的回答,您还需要将template&lt;T&gt; 添加到朋友std::os... 行,以便工作:

namespace rob {

   template <typename T> class Stack {
        friend std::ostream& operator<< (std::ostream& os, const Stack<T>& a) {
              return a.arr.print(os, ' ');
        }
   };

}

【讨论】:

  • 这工作得到链接器错误,但现在有一个 C2248 错误,说函数无法访问私有成员数据。使用@LibertyPaul 的回答。
  • @RobertHook 是导致此问题的模板。没有模板它可以访问 Stack 的私有成员
  • 使其工作的唯一方法是在类中编写 ostream 运算符的定义。我编辑我的答案
  • 我让它与模板 朋友 std::ostream& operator& a);在 h 和 template std::ostream& rob::operator& a) 在 hpp 上。谢谢!模板 让我不知所措。
猜你喜欢
  • 2011-10-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-03-04
  • 2011-04-28
  • 2011-05-08
  • 1970-01-01
相关资源
最近更新 更多