久久久久久久av_日韩在线中文_看一级毛片视频_日本精品二区_成人深夜福利视频_武道仙尊动漫在线观看

    <small id='geE8O'></small><noframes id='geE8O'>

    <i id='geE8O'><tr id='geE8O'><dt id='geE8O'><q id='geE8O'><span id='geE8O'><b id='geE8O'><form id='geE8O'><ins id='geE8O'></ins><ul id='geE8O'></ul><sub id='geE8O'></sub></form><legend id='geE8O'></legend><bdo id='geE8O'><pre id='geE8O'><center id='geE8O'></center></pre></bdo></b><th id='geE8O'></th></span></q></dt></tr></i><div class="qwawimqqmiuu" id='geE8O'><tfoot id='geE8O'></tfoot><dl id='geE8O'><fieldset id='geE8O'></fieldset></dl></div>
    1. <legend id='geE8O'><style id='geE8O'><dir id='geE8O'><q id='geE8O'></q></dir></style></legend>

      <tfoot id='geE8O'></tfoot>

        <bdo id='geE8O'></bdo><ul id='geE8O'></ul>

      鏈接器如何處理跨翻譯單元的相同模板實例化?

      How does the linker handle identical template instantiations across translation units?(鏈接器如何處理跨翻譯單元的相同模板實例化?)
        <tbody id='uDwFY'></tbody>

      <small id='uDwFY'></small><noframes id='uDwFY'>

    2. <i id='uDwFY'><tr id='uDwFY'><dt id='uDwFY'><q id='uDwFY'><span id='uDwFY'><b id='uDwFY'><form id='uDwFY'><ins id='uDwFY'></ins><ul id='uDwFY'></ul><sub id='uDwFY'></sub></form><legend id='uDwFY'></legend><bdo id='uDwFY'><pre id='uDwFY'><center id='uDwFY'></center></pre></bdo></b><th id='uDwFY'></th></span></q></dt></tr></i><div class="qwawimqqmiuu" id='uDwFY'><tfoot id='uDwFY'></tfoot><dl id='uDwFY'><fieldset id='uDwFY'></fieldset></dl></div>
        <bdo id='uDwFY'></bdo><ul id='uDwFY'></ul>

            1. <legend id='uDwFY'><style id='uDwFY'><dir id='uDwFY'><q id='uDwFY'></q></dir></style></legend><tfoot id='uDwFY'></tfoot>

                本文介紹了鏈接器如何處理跨翻譯單元的相同模板實例化?的處理方法,對大家解決問題具有一定的參考價值,需要的朋友們下面隨著小編來一起學習吧!

                問題描述

                限時送ChatGPT賬號..

                假設我有兩個翻譯單元:

                Suppose I have two translation-units:

                foo.cpp

                void foo() {
                  auto v = std::vector<int>();
                }
                

                bar.cpp

                void bar() {
                  auto v = std::vector<int>();
                }
                

                當我編譯這些翻譯單元時,每個單元都會實例化std::vector.

                When I compile these translation-units, each will instantiate std::vector<int>.

                我的問題是:這在鏈接階段是如何工作的?

                My question is: how does this work at the linking stage?

                • 兩個實例化的名稱是否不同?
                • 鏈接器是否將它們作為重復項刪除?

                推薦答案

                C++ 要求內聯函數定義存在于引用該函數的翻譯單元中.模板成員函數是隱式內聯的,但默認情況下也使用外部實例化連鎖.因此,當鏈接器可見時,定義的重復同一個模板用不同的模板參數實例化翻譯單位.鏈接器如何處理這種重復是您的問題.

                C++ requires that an inline function definition be present in a translation unit that references the function. Template member functions are implicitly inline, but also by default are instantiated with external linkage. Hence the duplication of definitions that will be visible to the linker when the same template is instantiated with the same template arguments in different translation units. How the linker copes with this duplication is your question.

                您的 C++ 編譯器受 C++ 標準約束,但您的鏈接器不受約束任何關于如何鏈接 C++ 的編纂標準:它本身就是一條法律,植根于計算歷史,對對象的源語言漠不關心編碼它鏈接.您的編譯器必須使用目標鏈接器可以并且將會這樣做,以便您可以成功鏈接您的程序并查看它們你期望什么.所以我將向您展示 GCC C++ 編譯器如何與用于處理不同翻譯單元中相同模板實例的 GNU 鏈接器.

                Your C++ compiler is subject to the C++ Standard, but your linker is not subject to any codified standard as to how it shall link C++: it is a law unto itself, rooted in computing history and indifferent to the source language of the object code it links. Your compiler has to work with what a target linker can and will do so that you can successfully link your programs and see them do what you expect. So I'll show you how the GCC C++ compiler interworks with the GNU linker to handle identical template instantiations in different translation units.

                該演示利用了一個事實,即 C++ 標準 要求 -根據一個定義規則- 同一模板的不同翻譯單元中的實例化相同的模板參數應具有相同的定義,編譯器 -當然 - 不能對不同之間的關系強制執行任何類似的要求翻譯單位.它必須信任我們.

                This demonstration exploits the fact that while the C++ Standard requires - by the One Definition Rule - that the instantiations in different translation units of the same template with the same template arguments shall have the same definition, the compiler - of course - cannot enforce any requirement like that on relationships between different translation units. It has to trust us.

                所以我們會用不同的參數實例化同一個模板翻譯單元,但我們會通過將宏控制的差異注入到隨后將展示的不同翻譯單元中的實現我們鏈接器選擇的定義.

                So we'll instantiate the same template with the same parameters in different translation units, but we'll cheat by injecting a macro-controlled difference into the implementations in different translation units that will subsequently show us which definition the linker picks.

                如果您懷疑此作弊使演示無效,請記住:編譯器無法知道 ODR 是否曾經在不同的翻譯單元中受到尊重,所以它在那個帳戶上的行為不會有所不同,并且沒有這樣的事情作為欺騙"鏈接器.無論如何,演示將證明它是有效的.

                If you suspect this cheat invalidates the demonstration, remember: the compiler cannot know whether the ODR is ever honoured across different translation units, so it cannot behave differently on that account, and there's no such thing as "cheating" the linker. Anyhow, the demo will demonstrate that it is valid.

                首先我們有我們的作弊模板標題:

                First we have our cheat template header:

                thing.hpp

                #ifndef THING_HPP
                #define THING_HPP
                #ifndef ID
                #error ID undefined
                #endif
                
                template<typename T>
                struct thing
                {
                    T id() const {
                        return T{ID};
                    }
                };
                
                #endif
                

                ID 的值是我們可以注入的跟蹤器值.

                The value of the macro ID is the tracer value we can inject.

                下一個源文件:

                foo.cpp

                #define ID 0xf00
                #include "thing.hpp"
                
                unsigned foo()
                {
                    thing<unsigned> t;
                    return t.id();
                }
                

                它定義了函數foo,其中thing是實例化定義t,返回t.id().通過成為一個函數實例化thing的外部鏈接,foo服務于目的的:-

                It defines function foo, in which thing<unsigned> is instantiated to define t, and t.id() is returned. By being a function with external linkage that instantiates thing<unsigned>, foo serves the purposes of:-

                • 強制編譯器完全實例化
                • 在鏈接中公開實例化,以便我們可以探查鏈接器會處理它.

                另一個源文件:

                boo.cpp

                #define ID 0xb00
                #include "thing.hpp"
                
                unsigned boo()
                {
                    thing<unsigned> t;
                    return t.id();
                }
                

                foo.cpp 一樣,只是它定義了 boo 代替了 foo 和設置 ID = 0xb00.

                which is just like foo.cpp except that it defines boo in place of foo and sets ID = 0xb00.

                最后一個程序源:

                ma??in.cpp

                #include <iostream>
                
                extern unsigned foo();
                extern unsigned boo();
                
                int main()
                {
                    std::cout << std::hex 
                    << '
                ' << foo()
                    << '
                ' << boo()
                    << std::endl;
                    return 0;
                }
                

                這個程序將以十六進制打印foo()的返回值——我們的作弊者應該這樣做= f00 - 然后是 boo() 的返回值 - 我們的作弊應該使 = b00.

                This program will print, as hex, the return value of foo() - which our cheat should make = f00 - then the return value of boo() - which our cheat should make = b00.

                現在我們將編譯 foo.cpp,我們將使用 -save-temps 來完成,因為我們想要看看程序集:

                Now we'll compile foo.cpp, and we'll do it with -save-temps because we want a look at the assembly:

                g++ -c -save-temps foo.cpp
                

                這會在 foo.s 中編寫程序集,感興趣的部分是thing::id() const 的定義(mangled = _ZNK5thingIjE2idEv):

                This writes the assembly in foo.s and the portion of interest there is the definition of thing<unsigned int>::id() const (mangled = _ZNK5thingIjE2idEv):

                    .section    .text._ZNK5thingIjE2idEv,"axG",@progbits,_ZNK5thingIjE2idEv,comdat
                    .align 2
                    .weak   _ZNK5thingIjE2idEv
                    .type   _ZNK5thingIjE2idEv, @function
                _ZNK5thingIjE2idEv:
                .LFB2:
                    .cfi_startproc
                    pushq   %rbp
                    .cfi_def_cfa_offset 16
                    .cfi_offset 6, -16
                    movq    %rsp, %rbp
                    .cfi_def_cfa_register 6
                    movq    %rdi, -8(%rbp)
                    movl    $3840, %eax
                    popq    %rbp
                    .cfi_def_cfa 7, 8
                    ret
                    .cfi_endproc
                

                頂部的三個指令很重要:

                Three of the directives at the top are significant:

                .section    .text._ZNK5thingIjE2idEv,"axG",@progbits,_ZNK5thingIjE2idEv,comdat
                

                這個將函數定義放在它自己稱為的鏈接部分中.text._ZNK5thingIjE2idEv 將輸出,如果需要,合并到.text(即代碼)目標文件鏈接的程序部分.一個像這樣的鏈接部分,即 .text. 被稱為 function-section.這是一個代碼部分,包含函數的定義.

                This one puts the function definition in a linkage section of its own called .text._ZNK5thingIjE2idEv that will be output, if it's needed, merged into the .text (i.e. code) section of program in which the object file is linked. A linkage section like that, i.e. .text.<function_name> is called a function-section. It's a code section that contains only the definition of function <function_name>.

                指令:

                .weak   _ZNK5thingIjE2idEv
                

                至關重要.它將 thing::id() const 分類為 weak> 符號.GNU 鏈接器識別 符號和 符號.對于強符號,鏈接器將只接受鏈接中的一個定義.如果有更多,它將給出多個-定義錯誤.但是對于弱符號,它可以容忍任意數量的定義,并選擇一個.如果一個弱定義的符號在鏈接中也有(只有一個)強定義,那么將選擇強定義.如果一個符號有多個弱定義而沒有強定義,然后鏈接器可以任意選擇任何弱定義.

                is crucial. It classifies thing<unsigned int>::id() const as a weak symbol. The GNU linker recognises strong symbols and weak symbols. For a strong symbol, the linker will accept only one definition in the linkage. If there are more, it will give a multiple -definition error. But for a weak symbol, it will tolerate any number of definitions, and pick one. If a weakly defined symbol also has (just one) strong definition in the linkage then the strong definition will be picked. If a symbol has multiple weak definitions and no strong definition, then the linker can pick any one of the weak definitions, arbitrarily.

                指令:

                .type   _ZNK5thingIjE2idEv, @function
                

                thing::id() 歸類為引用函數 - 而不是數據.

                classifies thing<unsigned int>::id() as referring to a function - not data.

                然后在定義體中,在地址處組裝代碼由弱全局符號_ZNK5thingIjE2idEv標記,本地相同標記為 .LFB2.代碼返回 3840 (= 0xf00).

                Then in the body of the definition, the code is assembled at the address labelled by the weak global symbol _ZNK5thingIjE2idEv, the same one locally labelled .LFB2. The code returns 3840 ( = 0xf00).

                接下來我們將以同樣的方式編譯boo.cpp:

                Next we'll compile boo.cpp the same way:

                g++ -c -save-temps boo.cpp
                

                再看看 thing::id()boo.s

                    .section    .text._ZNK5thingIjE2idEv,"axG",@progbits,_ZNK5thingIjE2idEv,comdat
                    .align 2
                    .weak   _ZNK5thingIjE2idEv
                    .type   _ZNK5thingIjE2idEv, @function
                _ZNK5thingIjE2idEv:
                .LFB2:
                    .cfi_startproc
                    pushq   %rbp
                    .cfi_def_cfa_offset 16
                    .cfi_offset 6, -16
                    movq    %rsp, %rbp
                    .cfi_def_cfa_register 6
                    movq    %rdi, -8(%rbp)
                    movl    $2816, %eax
                    popq    %rbp
                    .cfi_def_cfa 7, 8
                    ret
                    .cfi_endproc
                

                它是相同的,除了我們的作弊:這個定義返回 2816 (= 0xb00).

                It's identical, except for our cheat: this definition returns 2816 ( = 0xb00).

                當我們在這里時,讓我們注意一些可能不言而喻的事情:一旦我們進入匯編(或目標代碼),類就消失了.這里,我們的目標是:-

                While we're here, let's note something that might or might not go without saying: Once we're in assembly (or object code), classes have evaporated. Here, we're down to: -

                • 數據
                • 代碼
                • 符號,可以標記數據或標記代碼.

                所以這里沒有什么特別代表的實例化thing forT = 無符號.在這個例子中 thing 剩下的就是_ZNK5thingIjE2idEv 又名 thing::id() const 的定義.

                So nothing here specifically represents the instantiation of thing<T> for T = unsigned. All that's left of thing<unsigned> in this instance is the definition of _ZNK5thingIjE2idEv a.k.a thing<unsigned int>::id() const.

                現在我們知道編譯器在實例化thing方面做了什么在給定的翻譯單元中.如果必須實例化一個thing成員函數,然后組裝實例化成員的定義在標識成員函數的弱全局符號處函數,并且它將此定義放入其自己的函數部分.

                So now we know what the compiler does about instantiating thing<unsigned> in a given translation unit. If it is obliged to instantiate a thing<unsigned> member function, then it assembles the definition of the instantiated member function at a weakly global symbol that identifies the member function, and it puts this definition into its own function-section.

                現在讓我們看看鏈接器做了什么.

                Now let's see what the linker does.

                首先我們將編譯主源文件.

                First we'll compile the main source file.

                g++ -c main.cpp
                

                然后鏈接所有目標文件,請求對 _ZNK5thingIjE2idEv 進行診斷跟蹤,和一個鏈接映射文件:

                Then link all the object files, requesting a diagnostic trace on _ZNK5thingIjE2idEv, and a linkage map file:

                g++ -o prog main.o foo.o boo.o -Wl,--trace-symbol='_ZNK5thingIjE2idEv',-M=prog.map
                foo.o: definition of _ZNK5thingIjE2idEv
                boo.o: reference to _ZNK5thingIjE2idEv
                

                所以鏈接器告訴我們程序從foo.o 并在 boo.o調用.

                So the linker tells us that the program gets the definition of _ZNK5thingIjE2idEv from foo.o and calls it in boo.o.

                運行該程序表明它說的是實話:

                Running the program shows it's telling the truth:

                ./prog
                
                f00
                f00
                

                foo()boo() 都返回 thing().id() 的值 foo.cpp 中實例化.

                Both foo() and boo() are returning the value of thing<unsigned>().id() as instantiated in foo.cpp.

                thing::id() constother 定義變成了什么在 boo.o 中?地圖文件向我們展示了:

                What has become of the other definition of thing<unsigned int>::id() const in boo.o? The map file shows us:

                prog.map

                ...
                Discarded input sections
                 ...
                 ...
                 .text._ZNK5thingIjE2idEv
                                0x0000000000000000        0xf boo.o
                 ...
                 ...
                

                鏈接器刪除了 boo.o 中的函數部分包含另一個定義.

                The linker chucked away the function-section in boo.o that contained the other definition.

                現在讓我們再次鏈接 prog,但這次使用 foo.oboo.o 在倒序:

                Let's now link prog again, but this time with foo.o and boo.o in the reverse order:

                $ g++ -o prog main.o boo.o foo.o -Wl,--trace-symbol='_ZNK5thingIjE2idEv',-M=prog.map
                boo.o: definition of _ZNK5thingIjE2idEv
                foo.o: reference to _ZNK5thingIjE2idEv
                

                這一次,程序從boo.o中得到_ZNK5thingIjE2idEv的定義,然后在 foo.o 中調用它.程序確認:

                This time, the program gets the definition of _ZNK5thingIjE2idEv from boo.o and calls it in foo.o. The program confirms that:

                $ ./prog
                
                b00
                b00
                

                地圖文件顯示:

                ...
                Discarded input sections
                 ...
                 ...
                 .text._ZNK5thingIjE2idEv
                                0x0000000000000000        0xf foo.o
                 ...
                 ...
                

                鏈接器丟棄了函數部分 .text._ZNK5thingIjE2idEv來自 foo.o.

                that the linker chucked away the function-section .text._ZNK5thingIjE2idEv from foo.o.

                到此完成圖片.

                編譯器在每個翻譯單元中發出一個弱定義每個實例化的模板成員都在其自己的函數部分中.鏈接器然后只選擇它遇到的那些弱定義的第一個在需要解析弱引用的鏈接序列中象征.因為每個弱符號都指向一個定義,所以任何其中之一 - 特別是第一個 - 可用于解析所有引用到鏈接中的符號,其余的弱定義是消耗品.多余的弱定義必須被忽略,因為鏈接器只能鏈接給定符號的一個定義.還有多余的弱定義可以被鏈接器丟棄,無需任何擔保程序損壞,因為編譯器將每一個都單獨放置在一個鏈接部分中.

                The compiler emits, in each translation unit, a weak definition of each instantiated template member in its own function section. The linker then just picks the first of those weak definitions that it encounters in the linkage sequence when it needs to resolve a reference to the weak symbol. Because each of the weak symbols addresses a definition, any one one of them - in particular, the first one - can be used to resolve all references to the symbol in the linkage, and the rest of the weak definitions are expendable. The surplus weak definitions must be ignored, because the linker can only link one definition of a given symbol. And the surplus weak definitions can be discarded by the linker, with no collateral damage to the program, because the compiler placed each one in a linkage section all by itself.

                通過選擇它看到的第一個弱定義,鏈接器有效地隨機選擇,因為目標文件的鏈接順序是任意的.但這很好,只要我們遵守跨多個翻譯單元的 ODR,因為我們這樣做了,那么所有弱定義確實是相同的.#include 的通常做法是從頭文件中的任何地方使用類模板(并且在我們這樣做時不宏注入任何本地編輯)是遵守規則的一種相當可靠的方式.

                By picking the first weak definition it sees, the linker is effectively picking at random, because the order in which object files are linked is arbitrary. But this is fine, as long as we obey the ODR accross multiple translation units, because it we do, then all of the weak definitions are indeed identical. The usual practice of #include-ing a class template everywhere from a header file (and not macro-injecting any local edits when we do so) is a fairly robust way of obeying the rule.

                這篇關于鏈接器如何處理跨翻譯單元的相同模板實例化?的文章就介紹到這了,希望我們推薦的答案對大家有所幫助,也希望大家多多支持html5模板網!

                【網站聲明】本站部分內容來源于互聯網,旨在幫助大家更快的解決問題,如果有圖片或者內容侵犯了您的權益,請聯系我們刪除處理,感謝您的支持!

                相關文檔推薦

                Why do two functions have the same address?(為什么兩個函數的地址相同?)
                Why the initializer of std::function has to be CopyConstructible?(為什么 std::function 的初始化程序必須是可復制構造的?)
                mixing templates with polymorphism(混合模板與多態性)
                When should I use the keyword quot;typenamequot; when using templates(我什么時候應該使用關鍵字“typename?使用模板時)
                Dependent name resolution amp; namespace std / Standard Library(依賴名稱解析命名空間 std/標準庫)
                gcc can compile a variadic template while clang cannot(gcc 可以編譯可變參數模板,而 clang 不能)
                  <tbody id='WOoEF'></tbody>
              • <i id='WOoEF'><tr id='WOoEF'><dt id='WOoEF'><q id='WOoEF'><span id='WOoEF'><b id='WOoEF'><form id='WOoEF'><ins id='WOoEF'></ins><ul id='WOoEF'></ul><sub id='WOoEF'></sub></form><legend id='WOoEF'></legend><bdo id='WOoEF'><pre id='WOoEF'><center id='WOoEF'></center></pre></bdo></b><th id='WOoEF'></th></span></q></dt></tr></i><div class="qwawimqqmiuu" id='WOoEF'><tfoot id='WOoEF'></tfoot><dl id='WOoEF'><fieldset id='WOoEF'></fieldset></dl></div>

                <small id='WOoEF'></small><noframes id='WOoEF'>

                      <legend id='WOoEF'><style id='WOoEF'><dir id='WOoEF'><q id='WOoEF'></q></dir></style></legend>

                        • <bdo id='WOoEF'></bdo><ul id='WOoEF'></ul>
                        • <tfoot id='WOoEF'></tfoot>

                          主站蜘蛛池模板: 日本免费黄色一级片 | 婷婷久久网 | 9久9久9久女女女九九九一九 | 日本黄色大片免费 | 欧洲精品码一区二区三区免费看 | 国产日韩欧美一区二区在线播放 | 国产乱码精品一区二区三区忘忧草 | 日韩精品在线播放 | 国产精品久久久久久一级毛片 | 成人做爰69片免费观看 | 国产精品久久久久一区二区三区 | 亚洲国产成人av好男人在线观看 | 成人午夜激情 | 免费一看一级毛片 | 成人免费小视频 | 亚洲国产成人av好男人在线观看 | 二区av | 精品成人在线观看 | 欧美亚洲国产精品 | 91一区二区三区 | 午夜精品一区二区三区在线观看 | 97色免费视频 | 中文字幕在线电影观看 | 欧美成人自拍视频 | 天天艹| 亚洲人成网亚洲欧洲无码 | 中文在线一区二区 | 亚洲精品一区中文字幕乱码 | 91视频在线观看 | 欧美视频一级 | aaa天堂| 精品国产一区二区国模嫣然 | 欧美精品第一区 | 国产性生活一级片 | 精品欧美一区二区三区久久久小说 | 久久久高清 | 69性欧美高清影院 | 日韩在线播放中文字幕 | 二区中文| www.亚洲一区 | 国产一区在线看 |