【问题标题】:Linker failing to find overloaded operator链接器找不到重载的运算符
【发布时间】:2021-01-12 22:40:47
【问题描述】:

我正在尝试为我的一个类重载 << 运算符,但链接器始终无法找到重载。一直在网上搜索我错过的关于如何声明和实现运算符重载的任何内容,但对我来说似乎没有什么特别突出的。有什么想法可以解决这个问题吗?

Undefined symbols for architecture x86_64:
  "memath::operator<<(std::__1::basic_ostream<char, std::__1::char_traits<char> >&, memath::Vector3 const&)", referenced from:
      _main in mecli.cxx.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

vector3.h

#include <string>
#include <iostream>

namespace memath {

class Vector3 {

public:
    double x;
    double y;
    double z;

    Vector3();

    std::string to_string() const;

    friend std::ostream& operator<<(std::ostream &strm, const Vector3 &a);

};

};

vector3.cxx

#include <string>
#include <iostream>
#include <sstream>

#include "vector3.h"

using namespace memath;

Vector3::Vector3() : Vector3(0, 0, 0) {}

std::string Vector3::to_string() const {
    std::ostringstream r;
    r << "Vector3" << "(" << this->x << "," << this->y << "," << this->z << ")";
    return r.str();
}

std::ostream& operator<<(std::ostream &strm, const Vector3 &a) {
    strm << a.to_string();
    return strm;
}

mecli.cxx

#include <iostream>
#include <cstdlib>
#include <string>

#include "vector3.h"

int main(int argc, char** argv) {
    memath::Vector3 vec1;
    std::cout << vec1 << std::endl;
}

【问题讨论】:

    标签: c++ c++11 cmake clang linker-errors


    【解决方案1】:

    因为Vector3 在命名空间memath 中,朋友声明声明了memath::operator&lt;&lt;,但您随后定义了::operator&lt;&lt;。所以只要让定义与声明相匹配:

    std::ostream& memath::operator<<(std::ostream &strm, const Vector3 &a) {
    //            ^~~~~~~~
    

    后续问题,为什么using namespace memath; 在这种情况下似乎不在文件顶部,并且特别排除了此运算符重载?

    这不是这个运算符特有的。其实并不是专门针对运营商的。你会得到与函数相同的行为。而且它不是特定于友元声明的。

    让我们看一个更简单的例子:

    namespace ns
    {
        struct X
        {
            int foo(int);  // => ::ns::X::foo
        };
    
        int bar(int);      // => ::ns::bar
    }
    
    using namespace ns;
    
    int X::foo(int a) { return a + 1; } // (1)  => ::ns::X::foo
    int bar(int a) { return a * 2; }    // (2)  => ::bar
    

    就像在您的示例中一样,foo 按您的预期工作,但 bar 是模棱两可的,就像您的 operator&lt;&lt; 一样。那么两者有什么区别呢?这是一个简化的解释:

    (1):这是限定名称foo 的定义。 X:: 是它合格的原因。所以fooX 中搜索。但是X 是什么? X 是不合格的名称 ID。所以现在对X 执行了不合格的查找。这意味着 X 在当前命名空间(全局)和由 using 指令引入的所有命名空间中进行搜索。在这种情况下,X 只能在命名空间ns 中找到。所以X::foo 被解析为ns::X::foo,这使它成为ns::X 类的方法foo 的声明。

    (2) 这是非限定名称bar 的定义。由于bar 是不合格的,这被解释为在当前命名空间(全局命名空间)中名称bar 的声明。因为bar 是一个被声明的新名称,所以不执行查找。所以bar::bar的声明。

    请记住,这是一个简化的解释。

    【讨论】:

    • 太棒了,谢谢!后续问题,在这种情况下,为什么 using namespace memath; 在文件顶部似乎并不重要,并且特别排除了此运算符重载?我有点 C++ 菜鸟,我认为这将适用于整个文件。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-01-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多