【问题标题】:Why does inlining my accessors break my code?为什么内联访问器会破坏我的代码?
【发布时间】:2015-11-25 16:27:24
【问题描述】:

我遇到了一个奇怪的问题,尝试 inline 我的“Person”类的访问器会导致代码无法编译。

以下代码将成功编译并运行(使用 Visual Studio 2012):

Person.h

#pragma once
#include <string>

using namespace std;

class Person
{
public:
    Person(string name, int age = 0);
    ~Person(void);

    // Accessors
    string name(void) const;
    int    age (void) const;

private:
    string m_name;
    int    m_age;
};

Person.cpp

#include "stdafx.h"
#include "Person.h"


Person::Person(string name, int age) :
    m_name(name),
    m_age (age )
{}


Person::~Person(void) {}

string Person::name(void) const
{
    return m_name;
}

int Person::age(void) const
{
    return m_age;
}

header_test.cpp

#include "stdafx.h"
#include <iostream>
#include "Person.h"

using namespace std;

int _tmain(int argc, _TCHAR* argv[])
{
    Person p("Joe");

    cout << p.name() << endl;

    return 0;
}

如果我将访问器更改为 inline 函数代码会中断。

在 Person.h 中内联访问器

// Accessors
inline string name(void) const;
inline int    age (void) const;

在 Person.cpp 中内联访问器

inline string Person::name(void) const
{
    return m_name;
}

inline int Person::age(void) const
{
    return m_age;
}

这样做会产生以下错误:

1>header_test.obj:错误 LNK2019:未解析的外部符号“公共:类 std::basic_string,类 std::allocator > __thiscall Person::name(void)const” (?name@Person@@QBE?AV ?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@XZ) 在函数_wmain中引用

1>致命错误 LNK1120:1 个未解决的外部问题

上帝啊,那个错误信息太神秘了……谢谢你提供的所有这些哦,如此有用的信息 Microsoft/Visual Studio!


我知道inline 关键字只是对编译器的“提示”,在这里可能没有真正的价值,但它仍然不应该破坏代码!

为什么会这样?

【问题讨论】:

  • 内联函数应该在头文件中定义,而不是在单独的文件中。
  • [OT]:(void) 在 C++ 中不是必需的。
  • @Jarod42 我知道这不是必需的,但 Visual Studio 似乎默认将它放在那里(当它为你自动生成代码时)所以我只是保持一致。

标签: c++ visual-studio-2012 inline header-files member-functions


【解决方案1】:

我不是语言律师,所以我无法判断编译器的行为是否合法。然而,我可以解释发生了什么。

当你标记你的函数inline时,你不是暗示编译器它可以内联这个函数。由于超过 10 年的编译器在这里不需要您的提示。他们知道何时内联。相反,您所做的,是表明函数定义对于它所包含的每个翻译单元都是本地的。因为这个定义应该是可用的。

实际上,您所说的是 name() 定义对于每个 .cpp 文件都应该是本地的,但您并没有使其可用于每个 .cpp 文件!我仍然相信编译器可以在这里给出警告。

【讨论】:

  • 我也不是语言律师。我对您所说的内容略有不同的是,在定义它的模块中声明和定义inline 的函数不需要由该模块导出以用于其他模块。 ICC 中有一个编译时选项,我总是使用(我希望它是语言标准的一部分)说声明一个函数 inline and 使用它 and未定义它是编译时报告的错误,而不仅仅是可能的链接时错误。
  • @JSF,我想,这或多或少是我想说的。但是,不是不需要导出,而是不会导出
  • 我不会声称知道我使用的编译器是否错误,但我当然知道他们经常导出声明和定义的函数的定义inline .因此,当某些变化(通常与该函数无关)使编译器决定不导出它时,经验不足的程序员会感到困惑。
  • @JSF,没有。内联函数不能导出,这明显违反了一个定义规则。它们要么被完全删除为函数 - 通过真正内联它们 - 要么使用本地链接声明。
  • 也许我错误地使用了“导出”这个词(或者至少与您不同)。 a.cpp 声明 inline f() 并使用它但没有定义它。 b.cpp 声明 inline f() 并使用它并定义它。在链接时,b.cpp 中定义的f() 的副本可能对a.obj 可用也可能不可用。由于似乎与f() 无关的因素,该可用性会在以后的构建中发生变化。
【解决方案2】:

如果你想使用inline关键字,你需要在头部定义函数体。 inline 还不仅仅是给编译器一个提示:它或多或少地关闭了关于函数被定义一次且仅一次的“一个定义”规则*。

此外,如果您在标题中定义类成员函数,ala

class Foo {
    int bar() { return 5; }
};

默认情况下它们是“内联”的,因此没有理由输入关键字:-)

* 从技术上讲不是,但为简单起见,您可以将其视为这种行为。请参阅下面 SergeyA 的评论。

【讨论】:

  • 它不会“关闭”规则。它确保遵守规则。
  • 在回答非疯狂问题时,我不喜欢涉及太多技术细节,但你是对的。
猜你喜欢
  • 2012-08-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-02-25
  • 2021-02-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多