链接器找不到重载的运算符

Linker failing to find overloaded operator

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

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

因为 Vector3 在命名空间 memath 中,友元声明声明 memath::operator<<,但您随后定义 ::operator<<。所以只需使定义与声明相匹配:

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

Follow up question, why doesn't the using namespace memath; seem to matter at the top of the file in this case and exclude this operator overload in particular?

这不是这个操作员特有的。其实不是运营商特有的。你会得到与功能相同的行为。而且它并不特别适用于朋友声明。

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

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<< 一样。那么两者有什么区别呢?这是一个简化的解释:

(1):这是限定名的定义fooX:: 是它合格的原因。所以 fooX 内搜索。但是 X 是什么? X 是不合格的 name-id。所以现在对 X 执行了不合格的查找。这意味着 X 在当前命名空间(全局)和由 using 指令引入的所有命名空间中搜索。在这种情况下 X 仅在命名空间 ns 中找到。所以 X::foo 被解析为 ns::X::foo 这使得它成为 class ns::X.

的方法 foo 的声明

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

的声明

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