簡體   English   中英

G ++為未使用的模板特化生成代碼?

[英]G++ generates code for unused template specializations?

在我正在研究的項目的一些序列化代碼中,我有一個類型,其大小取決於編譯器。 為了解決這個問題,我決定使用模板專業化,這非常有效。 一切都在編譯時解決。 代碼看起來有點像這樣(不是真正的代碼,只是一個例子):

template <int size>
void
special_function()
{
     std::cout << "Called without specialization: " << size << std::endl;
}

template <>
void
special_function<4>()
{
     std::cout << "dword" << std::endl;
}

template <>
void
special_function<8>()
{
     std::cout << "qword" << std::endl;
}

int
main()
{
     special_function<sizeof(int)>();
     return 0;
}

在我的32位系統上,按預期執行上述程序輸出dword 但這樣做的全部意義並不是只做if (sizeof(int) == 4) { ... } else if ...是我希望編譯器只為適當的函數生成代碼。 由於special_function<4>是本程序中唯一調用的函數,因此我預計它是編譯器生成的唯一函數(本例中為gcc 4.1.2,在x86 Linux上)。

但這不是觀察到的行為。

雖然它確實有效,但每個模板專業化的代碼都會生成,盡管從未使用過。 但是,不生成通用定義。

我應該提一下,這是一步編譯,而不是編譯到中間對象文件后跟一個鏈接。 在這種情況下,將死代碼刪除推遲到鏈接階段似乎很自然,我知道鏈接器並不總是非常擅長這一點。

有誰知道發生了什么? 我在這里缺少模板專業化的微妙之處嗎? 上帝知道魔鬼在C ++的細節中。

編輯:因為它已被提及,這種行為發生在-O3和-Os。

EDIT2:Rob建議將函數放在匿名命名空間中。 這樣做並使用任何級別的優化進行編譯確實會刪除死代碼,這很好。 但我很好奇,所以我嘗試用以下程序做同樣的事情:

namespace {
void foo() { std::cout << "Foo!" << std::endl; }
void bar() { std::cout << "Bar!" << std::endl; }
}

int
main()
{
       foo();
       return 0;
}

這里的想法是看看Rob的解決方案是否實際上與模板特化相關。 事實證明,上面編譯時啟用了優化的代碼省略了可執行文件中未使用的bar()定義。 因此,雖然他的答案解決了我的直接問題,但它並沒有解釋為什么根本不編譯未使用的模板特化。

有沒有人知道標准中的相關片段可以解釋這一點? 我一直認為模板只是在使用時生成的,但對於完全專業化可能並非如此......

示例中的模板特化是具有外部鏈接的函數。 編譯器無法知道不會從另一個翻譯單元調用它們。

在我的g ++ 4.7.2 Ubuntu系統上,將模板放入匿名命名空間並使用-O3編譯可防止生成未使用的函數。

同樣,聲明函數模板static產生預期的效果。

這是一個特殊的問題。 我稍微研究了一下,這個問題與模板專業化無關。 我猜g ++默認情況下不會刪除未使用的符號。 如果您以后想要將輸出鏈接到另一個程序,這是有道理的。

但是,您可以使用命令行選項來刪除未使用的符號。 有關詳細信息,請參閱此帖子:

如何用GCC和ld刪除未使用的C / C ++符號?

但也看到了這里

使用GCC查找無法訪問的函數(“死代碼”)

和這里

傳統C / C ++項目中的死代碼檢測

為了試試這個,我修改了代碼如下:

#include <iostream>

void junk_function() {
    std::cout<<"test" << std::endl;    
}

template <int size>
void special_function()
{
     std::cout << "Called without specialization: " << size << std::endl;
}

template <>
void special_function<4>()
{
     std::cout << "dword" << std::endl;
}

template <>
void special_function<8>()
{
     std::cout << "qword" << std::endl;
}

int main()
{
     special_function<sizeof(int)>();
     return 0;
}

然后將此代碼存儲到sp.cpp。 第一,

g++ -Os sp.cpp -o sp
nm sp

得到了這個(注意,為了便於閱讀,我刪除了一堆符號):

0804879a T _Z13junk_functionv
080487b8 T _Z16special_functionILi4EEvv
080487f5 T _Z16special_functionILi8EEvv

似乎有兩個未使用的符號。 我也嘗試了-O1,-O2,-O3,並且得到了相同的結果。

下一個:

g++ -Os -fdata-sections -ffunction-sections sp.cpp -o sp -Wl,--gc-sections
nm sp

得到了這個:

0804875a T _Z16special_functionILi4EEvv

而已。 所以看起來你只需要傳遞正確的參數來告訴g ++去除未使用的符號。 在Mac上,我猜他們有-dead_strip選項,但我不知道為什么它在g ++中不起作用(盡管在手冊頁中提到過。不可否認,我沒有深入研究這個,所以有可能是一個我錯過的精美印刷品。

我認為鏈接時默認情況下Visual C ++的鏈接器會剝離,但我沒有測試。 也許別人可以插話。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM