为什么在将c样式数组传递给期望std :: string的方法时会出现链接器问题?

时间:2011-04-01 19:12:00

标签: c++ string gcc linker g++

我有一个Logger类,它希望以下列方式使用std :: string:

class Logger
{
public:
    void Log(const std::string message);
};

另一个类称它为:

class MyClass
{
public:
    void MyMethod()
    {
        Logger logger;
        logger.Log("Hello World!"); // This line is causing linker error.

        // std::string myString("Hello World!");   If this and the line below are
        // logger.Log(myString);                   uncommented, I get no errors.
    }
};

错误消息是:

Logger.cpp: undefined reference to `Logger::Log(std::basic_string<char, std::char_traits<char>, std::allocator<char> >)`

显然链接器知道Log方法,因为如上所示,注释掉的行将允许它正确链接。

有没有人知道发生了什么?如果需要更多信息,请与我们联系。

这是在Linux下使用gcc 4.4.3(使用Eclipse)

编辑:

我的理解是你总是可以将c风格的字符串传递给期望std :: string的方法,因为std :: string有一个合适的拷贝构造函数来处理它。

EDITx2:

Facepalm时刻。我发现了错误。感谢大家让我更加努力地看待我的代码。请参阅下面的完整解决方案。

这段代码应该在问题中添加:

// cpp file

// This should have been in the header, not the cpp file.
inline void Logger::Log(const std::string message)
{
  /* do sutff */
}

5 个答案:

答案 0 :(得分:1)

您的问题是您尚未定义Log(又名Logger::Log)方法。你已经声明了它,但没有在任何地方定义它。

另一个小问题是你真的应该这样声明:

void Log(const ::std::string &message);

没有特别好的理由强迫来电者复制他们传递给你的字符串。

有一些可能性可以解释为什么你可能认为它被定义,即使它不是。

首先,如果定义了函数,您可能无法链接到与.o文件对应的相应.cpp文件中。这意味着您已定义它,但链接器不知道您的定义。

另一种可能性是你定义了一个名称相似的函数,该函数与被调用的函数不同。

另一种可能性是您已将函数定义为内联函数。如果函数是内联定义的,则编译器可能没有为其发出代码。如果定义在引用时可用,则许多编译器仅为内联函数发出代码,并且引用方式要求函数在某处具有实际代码(例如获取其地址)。

如果你有一个定义是内联的函数,那么该函数通常应该在头文件中,否则inline关键字是无用的,甚至可能会导致你的问题,因为你的所有其他代码都会假设实际代码驻留在支持函数声明的特定地址上,除了它出现的一个.cpp文件,它可能不会强制代码被发出引用该函数。

答案 1 :(得分:1)

您似乎忘了创建Logger::Log功能,或者您没有向我们展示char*的重载,或者您没有链接Logger.o它失败了。

答案 2 :(得分:1)

好的,对不起,我觉得自己很傻。

class Logger
{
public:
    void Log(const std::string message);
}

Log方法位于其cpp文件中:

inline void Log(const std::string message) { /* stuff */ }

因为它是在cpp文件中内联声明的,所以链接器没有找到它。感谢大家不断推动我更加努力地看待签名。

对于那些好奇的人来说,修复它的方法是将方法实现移动到头文件中,以便它可以保持内联,或者删除内联修饰符。

答案 3 :(得分:0)

您的示例代码在VC ++ 2010和gcc 4.1.2中编译和链接很好,只要我提供Logger :: Log()方法的定义,无论是否有注释掉的代码部分。

您确定已定义Logger :: Log()方法吗?您是否尝试在尽可能小的程序中复制问题?如果它在小测试用例中工作,那么在原始代码中会出现更微妙的问题。

你提到的另一个(远程)可能性,当你取消注释它链接正常的两行时,由于某种原因,链接器正在优化Logger :: Log(const std :: string)方法。

答案 4 :(得分:0)

实际上,您的会员功能声明应为

void Log(const std :: string&amp; message);

请记住,按值传递参数意味着它们将被复制。如果你只是稍微记录一下,它就没有什么区别,但如果你记录的事件发生率很高,那么字符串的分配/复制就会开始造成损失。

它必须是const,因为记录器不应该修改传入的消息,并且在传入const char *参数时会阻止编译器抱怨。