C ++的內聯是不是完全可選的?

[英]Isn't C++'s inline totally optional?


I have a class that had an inline member, but I later decided that I wanted to remove the implementation from the headers so I moved the members body of the functions out to a cpp file. At first I just left the inlined signature in the header file (sloppy me) and the program failed to link correctly. Then I fixed my header and it all works fine, of course.

我有一個有內聯成員的類,但后來我決定從頭中刪除實現,所以我將函數的成員體移到cpp文件中。剛開始我剛剛在頭文件中留下了內聯簽名(草率我),程序無法正確鏈接。然后我修好了標題,當然一切正常。

But wasn't inline totally optional?

但是不是內聯完全可選嗎?

In code:

First:

//Class.h
class MyClass
{
   void inline foo()
   {}
};

Next changed to (won't link):

接下來改為(不會鏈接):

//Class.h
class MyClass
{
   void inline foo();
};

//Class.cpp
void MyClass::foo()
{}

And then to (will work fine):

然后(將工作正常):

//Class.h
class MyClass
{
   void foo();
};

//Class.cpp
void MyClass::foo()
{}

I thought inline was optional, and imagined I might get by with a warning for my sloppiness, but didn't expect a linking error. What's the correct/standard thing a compiler should do in this case, did I deserve my error according to the standard?

我認為內聯是可選的,並且想象我可能會因為我的邋iness而警告,但沒想到鏈接錯誤。在這種情況下編譯器應該做的正確/標准的事情是什么,我是否應該根據標准得到我的錯誤?

4 个解决方案

#1


35  

Indeed, there is this one definition rule saying that an inline function must be defined in every translation unit it is used. Gory details follow. First 3.2/3:

實際上,存在這樣一個定義規則,即必須在使用它的每個翻譯單元中定義內聯函數。血腥細節如下。第3.2 / 3號:

Every program shall contain exactly one definition of every non-inline function or object that is used in that program; no diagnostic required. The definition can appear explicitly in the program, it can be found in the standard or a user-defined library, or (when appropriate) it is implicitly defined (see 12.1, 12.4 and 12.8). An inline function shall be defined in every translation unit in which it is used.

每個程序應該只包含該程序中使用的每個非內聯函數或對象的一個​​定義;無需診斷。該定義可以在程序中明確顯示,可以在標准或用戶定義的庫中找到,或者(在適當的時候)隱式定義(見12.1,12.4和12.8)。內聯函數應在每個使用它的翻譯單元中定義。

And of course 7.1.2/4:

當然還有7.1.2 / 4:

An inline function shall be defined in every translation unit in which it is used and shall have exactly the same definition in every case (3.2). [Note: a call to the inline function may be encountered before its definition appears in the translation unit. ] If a function with external linkage is declared inline in one translation unit, it shall be declared inline in all translation units in which it appears; no diagnostic is required. An inline function with external linkage shall have the same address in all translation units. A static local variable in an extern inline function always refers to the same object. A string literal in an extern inline function is the same object in different translation units.

內聯函數應在每個使用它的翻譯單元中定義,並且在每種情況下都應具有完全相同的定義(3.2)。 [注意:在定義出現在翻譯單元之前,可能會遇到對內聯函數的調用。 ]如果在一個翻譯單元中內聯聲明具有外部鏈接的功能,則應在其出現的所有翻譯單元中內聯聲明;無需診斷。具有外部鏈接的內聯函數在所有翻譯單元中應具有相同的地址。外部內聯函數中的靜態局部變量始終引用同一對象。 extern內聯函數中的字符串文字是不同翻譯單元中的同一對象。

However, if you define your function within the class definition, it is implicitly declared as inline function. That will allow you to include the class definition containing that inline function body multiple times in your program. Since the function has external linkage, any definition of it will refer to the same function (or more gory - to the same entity).

但是,如果在類定義中定義函數,則將其隱式聲明為內聯函數。這將允許您在程序中多次包含包含該內聯函數體的類定義。由於該函數具有外部鏈接,因此它的任何定義都將引用相同的函數(或更多血腥 - 指向同一實體)。

Gory details about my claim. First 3.5/5:

關於我的主張的血腥細節。第3.5 / 5:

In addition, a member function, static data member, class or enumeration of class scope has external linkage if the name of the class has external linkage.

此外,如果類的名稱具有外部鏈接,則成員函數,靜態數據成員,類范圍的類或枚舉具有外部鏈接。

Then 3.5/4:

A name having namespace scope has external linkage if it is the name of [...] a named class (clause 9), or an unnamed class defined in a typedef declaration in which the class has the typedef name for linkage purposes.

具有命名空間范圍的名稱具有外部鏈接(如果它是命名類的名稱(第9節)),或者在typedef聲明中定義的未命名類,其中類具有用於鏈接目的的typedef名稱。

This "name for linkage purposes" is this fun thing:

這個“用於鏈接目的的名稱”是這個有趣的事情:

typedef struct { [...] } the_name;

Since now you have multiple definitions of the same entity in your programs, another thing of the ODR happens to restrict you. 3.2/5 follows with boring stuff.

從現在開始,您的程序中有多個同一實體的定義,ODR的另一個內容恰好限制了您。 3.2 / 5跟隨無聊的東西。

There can be more than one definition of a class type (clause 9), enumeration type (7.2), inline function with external linkage (7.1.2) [...] in a program provided that each definition appears in a different translation unit, and provided the definitions satisfy the following requirements. Given such an entity named D defined in more than one translation unit, then

可以有多個類類型的定義(第9節),枚舉類型(7.2),帶有外部鏈接的內聯函數(7.1.2)[...]在程序中,每個定義出現在不同的翻譯單元中,並且定義滿足以下要求。鑒於這樣一個名為D的實體在多個翻譯單元中定義,那么

  • each definition of D shall consist of the same sequence of tokens; and
  • D的每個定義應由相同的令牌序列組成;和

  • in each definition of D, corresponding names, looked up according to 3.4, shall refer to an entity defined within the definition of D, or shall refer to the same entity, after overload resolution (13.3) and after matching of partial template specialization (14.8.3) [...]
  • 在D的每個定義中,根據3.4查找的相應名稱,應指在D的定義中定義的實體,或者在重載決議(13.3)之后和部分模板專門化匹配之后應引用同一實體(14.8) .3)[...]

I cut off some unimportant stuff now. The above are the two important one to remember about inline functions. If you define an extern inline function multiple times, but do define it differently, or if you define it and names used within it resolve to different entities, then you are doing undefined behavior.

我現在切斷了一些不重要的東西。以上是關於內聯函數的兩個重要記憶。如果您多次定義extern內聯函數,但確實以不同方式定義它,或者如果您定義它並且其中使用的名稱解析為不同的實體,那么您正在執行未定義的行為。

The rule that the function has to be defined in every TU in which it is used is easy to remember. And that it is the same is also easy to remember. But what about that name resolution thingy? Here some example. Consider a static function assert_it:

必須在使用它的每個TU中定義函數的規則易於記憶。並且這也是容易記住的。但那個名字解析怎么樣呢?這里有一些例子。考慮一個靜態函數assert_it:

static void assert_it() { [...] }

Now, since static will give it internal linkage, when you include it into multiple translation units, then each definition will define a different entity. This means that you are not allowed to use assert_it from an extern inline function that's going to be defined multiple times in the program: Because what happens is that the inline function will refer to one entity called assert_it in one TU, but to another entity of the same name in another TU. You will find that this all is boring theory and compilers won't probably complain, but i found this example in particular shows the relation between the ODR and entities.

現在,由於static將為其提供內部鏈接,當您將其包含在多個翻譯單元中時,每個定義將定義一個不同的實體。這意味着您不允許在程序中多次定義的extern內聯函數中使用assert_it:因為內聯函數將在一個TU中引用一個名為assert_it的實體,但是引用另一個實體在另一個TU中使用相同的名稱。你會發現這一切都是無聊的理論和編譯器不會抱怨,但我發現這個例子特別顯示了ODR和實體之間的關系。


What follows is getting back to your particular problem again.

接下來是再次回到您的特定問題。

Following are the same things:

以下是相同的事情:

struct A { void f() { } };
struct A { inline void f(); }; void A::f() { } // same TU!

But this one is different, since the function is non-inline. You will violate the ODR, since you have more than one definition of f if you include the header more than once

但是這個是不同的,因為函數是非內聯的。您將違反ODR,因為如果您不止一次包含標題,則您有多個f的定義

struct A { void f(); }; void A::f() { } // evil!

Now if you put inline on the declaration of f inside the class, but then omit defining it in the header, then you violate 3.2/3 (and 7.1.2/4 which says the same thing, just more elaborating), since the function isn't defined in that translation unit!

現在,如果你把內聯的f語句放入內聯,但是然后省略在標題中定義它,那么你違反3.2 / 3(和7.1.2 / 4這說同樣的事情,只是更精細),因為函數沒有在該翻譯單元中定義!

Note that in C (C99), inline has different semantics than in C++. If you create an extern inline function, you should first read some good paper (preferably the Standard), since those are really tricky in C (basically, any used inline-definition of a function will need another, non-inline function definition in another TU. static inline functions in C are easy to handle. They behave like any other function, apart of having the usual "inline substitution" hint. static inline in both C and C++ serve only as a inline-substitution hint. Since static will already create a different entity any time it's used (because of internal linkage), inline will just add the inline-substitution hint - not more.

請注意,在C(C99)中,內聯與C ++中的語義不同。如果你創建一個extern內聯函數,你應該首先閱讀一些好的論文(最好是標准的),因為那些在C中真的很棘手(基本上,任何使用的函數的內聯定義都需要另一個非內聯函數定義) TU .C中的靜態內聯函數很容易處理。它們的行為與任何其他函數一樣,除了具有通常的“內聯替換”提示之外.C和C ++中的靜態內聯僅用作內聯替換提示。由於靜態已經在任何時候使用它(因為內部鏈接)創建一個不同的實體,內聯將只添加內聯替換提示 - 而不是更多。

#2


10  

Whether or not the method is actually inlined is at the sole discretion of the compiler. However the presence of the inline keyword will also affect the linkage of the method.

該方法是否實際內聯是由編譯器自行決定的。但是,內聯關鍵字的存在也會影響方法的鏈接。

C++ linkage is not my specialty so I'll defer to the links for a better explanation.

C ++鏈接不是我的專長所以我會推遲鏈接以獲得更好的解釋。

Alternately you can just wait for litb to provide the gory details in an hour or so ;)

或者,您可以等待litb在一小時左右提供血腥細節;)

#3


6  

Point to note: when method is declared inline, its definition MUST be together with its declaration.

注意:當方法聲明為內聯時,其定義必須與聲明一起。

#4


2  

Regarding harshath.jr's answer, a method need not be declared inline if its definition has the "inline" keyword, and that definition is available in the same header, i.e.:

關於harshath.jr的答案,如果一個方法的定義具有“inline”關鍵字,並且該定義在同一個標​​題中可用,則不需要內聯聲明該方法,即:

class foo
{
  void bar();
};

inline void foo::bar()
{
  ...
}

This is useful for conditionally inlining a method depending on whether or not the build is "debug" or "release" like so:

這對於有條件地內聯方法非常有用,具體取決於構建是“調試”還是“釋放”,如下所示:

// Header - foo.h

class foo
{
  void bar();  // Conditionally inlined.
};

#ifndef FOO_DEBUG
# include "foo.inl"
#endif

The "inline" file could look like:

“內聯”文件可能如下所示:

// Inline Functions/Methods - foo.inl
#ifndef FOO_DEBUG
# define FOO_INLINE inline
#else
# define FOO_INLINE
#endif

FOO_INLINE void foo::bar()
{
  ...
}

and the implementation could like the following:

並且實現可能如下:

// Implementation file - foo.cpp
#ifdef FOO_DEBUG
# include "foo.inl"
#endif

...

It's not exactly pretty but it has it's uses when aggressive inline becomes a debugging headache.

它並不完全漂亮,但是當攻擊性內聯成為一個調試問題時,它有它的用途。


注意!

本站翻译的文章,版权归属于本站,未经许可禁止转摘,转摘请注明本文地址:https://www.itdaan.com/blog/2009/05/26/724fe510394c8e2d827d83e1ab3ca9f2.html



 
粤ICP备14056181号  © 2014-2020 ITdaan.com