9

I am experiencing a strange issue where attempting to inline the accessors for my "Person" class causes the code to fail to compile.

The following code will compile and run successfully (Using 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;
}

If I change my accessors to be defined as inline functions the code breaks.

Inlining the accessors in Person.h

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

Inlining the accessors in Person.cpp

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

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

Doing this produces the following errors:

1>header_test.obj : error LNK2019: unresolved external symbol "public: class std::basic_string,class std::allocator > __thiscall Person::name(void)const " (?name@Person@@QBE?AV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@XZ) referenced in function _wmain

1>fatal error LNK1120: 1 unresolved externals

God that error message is cryptic... Thank you for all of that oh so useful information Microsoft/Visual Studio!


I know the inline keyword is just a "hint" to the compiler and probably has no real value here, but it still shouldn't break the code!

Why is this happening?

tjwrona1992
  • 8,614
  • 8
  • 35
  • 98

2 Answers2

3

I am not a language lawyer, so I can't tell if the compiler behaviour is legitimate or not. Yet, I can explain what is happening.

When you are marking your functions inline, you are not hinting the compiler that it can inline this function. Since over 10 years compilers do not need your hint here. They know when to inline. Instead, what you do, you indicate the function definition to be local for every translation unit it is included in. For this definition should be available.

Effectively what you said is that name() definition should be local for every .cpp file, but you didn't make it available for every .cpp file! I still believe the compiler could give a warning here.

cadaniluk
  • 15,027
  • 2
  • 39
  • 67
SergeyA
  • 61,605
  • 5
  • 78
  • 137
  • I'm also not a language lawyer. My slightly different spin on what you said is that a function that is declared and defined `inline` in the module where it is defined doesn't need to be exported by that module for use in other modules. There is a compile time option in ICC, that I always use (I wish it were part of the language standard) saying that declaring a function `inline` **and** using it **and** not defining it is a compile time reported error, rather than just a possible link time error. – JSF Nov 25 '15 at 16:38
  • @JSF, I think, this is more or less what I tried to say. However, not **doesn't need to be exported**, but **will not be exported**. – SergeyA Nov 25 '15 at 16:40
  • I won't claim to know whether the compilers I've used were **wrong**, but I certainly know they often export the definition of a function declared and defined `inline`. So when something changes (often with no connection to that function) making the compiler decide not to export it, less experienced programmers get confused. – JSF Nov 25 '15 at 16:46
  • @JSF, no. inline functions can not be exported, that would be a clear violation of one definition rule. They are either obliterated as functions altogether - by trully inlining them - or declared with local linkage. – SergeyA Nov 25 '15 at 16:52
  • Maybe I'm using the word "exported" incorrectly (or at least differently than you are). a.cpp declares `inline f()` and uses it and doesn't define it. b.cpp declares `inline f()` and uses it and does define it. At link time, the copy of `f()` defined in b.cpp might or might not be available for the use by a.obj. That availability then changes in a later build due to factors that seem to have no connection to `f()`. – JSF Nov 25 '15 at 16:57
  • @JSF, what you seem to be telling seems to be exact replica of original OP question, and it shows that it f() is not available during linktime exactly because it is not exported. – SergeyA Nov 25 '15 at 17:02
1

You need to define the function body in the header if you want to use the inline keyword. inline also does more than just give a hint to the compiler: it more or less shuts down the "one definition" rule* about functions being defined once and only once.

Furthermore, if you define class member functions inside the headers, ala

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

they get "inlined" by default, so there's no reason to type the keyword out :-)

* Technically not, but for simplicity you can think of it as behaving that way. See the comment below by SergeyA.

bstamour
  • 7,746
  • 1
  • 26
  • 39