【问题标题】:Why am I getting linker issues when passing a c-style array to a method expecting a std::string?为什么将 c 样式数组传递给需要 std::string 的方法时出现链接器问题?
【发布时间】:2011-04-01 19:12:00
【问题描述】:

我有一个 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:

捂脸时刻。我发现了错误。感谢大家让我更仔细地查看我的代码。请参阅下面的完整解决方案。

这段代码应该已经添加到问题中了:

// cpp file

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

【问题讨论】:

  • 您能否验证您的 Log 函数上的签名?大多数人把它写成 const std::string& 或 std::string,而不是 const std::string
  • 它是正确的。起初我将它作为 const std::string& ,但将其更改为 const std::string 认为是引用导致了问题。

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


【解决方案1】:

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

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

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

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

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

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

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

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

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

【讨论】:

  • @Klaim - 我在第一次回复时错误地输入了“构造函数”,因为LogLogger 是相似的名称,我很着急。现在已修复,我向您保证 Logger::Log 的缺失定义是问题所在。
【解决方案2】:

您似乎忘记创建 Logger::Log 函数,或者您没有向我们展示 char* 的重载,或者您在失败的情况下没有链接 Logger.o

【讨论】:

    【解决方案3】:

    好吧,对不起,我觉得自己像个傻瓜。

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

    Log 方法位于其 cpp 文件中:

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

    因为它在 cpp 文件中被声明为内联,所以链接器没有找到它。感谢大家不断推动我更仔细地查看签名。

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

    【讨论】:

    • 你应该选择一个答案并接受它,或者至少投票给那些给你有用建议的人。 :-)
    • 选择接受答案的问题在于,从技术上讲,目前他们都没有回答提出的问题,这就是我自己发布答案的原因。如果有人想用一个非常简洁的解释来重新解释我的答案,为什么内联方法必须在头文件而不是 cpp 文件中,我肯定会接受。
    • 我已根据您的建议修改了答案。 :-)
    【解决方案4】:

    只要我提供 Logger::Log() 方法的定义(带或不带注释掉的代码部分),您的示例代码就可以在 VC++ 2010 和 gcc 4.1.2 中正常编译和链接。

    你确定你已经定义了 Logger::Log() 方法吗?您是否尝试过在尽可能小的程序中复制问题?如果它在小型测试用例中有效,那么在您的原始代码中存在更微妙的问题。

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

    【讨论】:

      【解决方案5】:

      其实你的成员函数的声明应该是

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

      请记住,按值传递参数意味着它们将被复制。如果您只记录一点点,它不会有任何影响,但如果您记录以高速率发生的事件,则字符串的分配/复制将开始产生影响。

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

      【讨论】:

        猜你喜欢
        • 2011-11-29
        • 1970-01-01
        • 1970-01-01
        • 2020-12-11
        • 2011-01-06
        • 1970-01-01
        • 2013-08-14
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多