9
votes

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?

2
Inline functions should be defined inside a header file, and not in a separate file.Algirdas Preidžius
[OT]: (void) is not required in C++.Jarod42
@Jarod42 I'm aware it's not required, but Visual Studio seems to put it there by default (when it autogenerates code for you) so I was just being consistent.tjwrona1992

2 Answers

3
votes

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.

1
votes

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.