9

У меня возникла странная проблема, когда при попытке использовать inline аксессоры для моего класса «Человек» заставляют код не скомпилироваться.Почему встраивание моих аксессуаров нарушает мой код?

Следующий код будет компилироваться и работать успешно (с помощью 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: неразрешенный внешний символ "public: class std :: basic_string, class std :: allocator> __thiscall Person :: name (void) const" (? Name @ Person @@ QBE? AV? $ basic_string @ DU $ char_traits @ D @ станд @@ V $ распределитель @ D @ 2 @@ станд @@ XZ) упоминаться в функции _wmain

1> фатальной LNK1120 ошибки:? 1 неразрешенные внешние

Бог, что сообщение об ошибке является загадочным ... Спасибо за всю эту полезную информацию Microsoft/Visual Studio!


Я знаю, что ключевое слово inline просто «намек» на компилятор и, вероятно, не имеет никакого реального значения здесь, но она до сих пор не должны нарушать код!

Почему это происходит?

+2

Встроенные функции должны быть определены внутри файла заголовка, а не в отдельном файле. –

+1

[OT]: '(void)' не требуется в C++. – Jarod42

+0

@ Jarod42 Я знаю, что это не требуется, но Visual Studio, кажется, поместила его по умолчанию (когда он автоматически генерирует код для вас), поэтому я просто был последовательным. – tjwrona1992

ответ

3

Я не юрист на языке, поэтому я не могу сказать, является ли поведение компилятора законным или нет. Тем не менее, я могу объяснить, что происходит.

Когда вы отмечаете свои функции inline, вы являетесь не, намекая компилятору, что он может встроить эту функцию. Поскольку более 10 лет компиляторам не нужен ваш намек. Они знают, когда делать. Вместо этого, что вы делаете, вы указываете, что определение функции является локальным для каждой единицы перевода, в которую она включена.Для этого определения должно быть доступно.

Фактически вы сказали, что определение name() должно быть локальным для каждого файла .cpp, но вы не сделали его доступным для каждого файла .cpp! Я все еще верю, что компилятор может дать предупреждение здесь.

+0

Я тоже не юрист. Я немного отличаюсь от того, что вы сказали, что функция, объявленная и определенная 'inline' в модуле, где она определена, не должна экспортироваться этим модулем для использования в других модулях. В ICC есть опция времени компиляции, которую я всегда использую (я хотел бы, чтобы это было частью стандартного языка), заявив, что объявление функции 'inline' ** и ** с ее использованием ** и **, не определяющее ее, является компиляцией а не только возможную ошибку времени связи. – JSF

+0

@JSF, я думаю, это более или менее то, что я пытался сказать. Однако ** не нужно экспортировать **, но ** не будет экспортироваться **. – SergeyA

+0

Я не буду утверждать, что компиляторы, которые я использовал, были ** неправильными **, но я, конечно, знаю, что они часто экспортируют определение объявленной функции и определяют 'inline'. Поэтому, когда что-то меняется (часто без подключения к этой функции), что делает компилятор решим не экспортировать его, менее опытные программисты путаются. – JSF

1

Вам нужно определить тело функции в заголовке, если вы хотите использовать ключевое слово inline. inline также делает больше, чем просто дать подсказку компилятору: он более или менее отключает правило «одного определения» * о функциях, определяемых один раз и только один раз.

Кроме того, если определить функции-члены класса внутри заголовков, ала

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

они получают «встраиваются» по умолчанию, так что нет никаких причин, чтобы ввести ключевое слово из :-)

* Технически нет, но для простоты вы можете думать об этом как о том, как себя вести. См. Комментарий Сергея.

+0

Это не «выключает» правило. Это обеспечивает соблюдение правила. – SergeyA

+0

Мне не нравится получать слишком много технических подробностей, когда вы получаете ответ на не сумасшедшие вопросы, но вы правы. – bstamour