當頭檔案包含如下模板變數時會發生什么:
template <class T>
std::map<T, std::string> errorCodes = /* some initialization logic */;
這個變數使用安全嗎?對此進行了一些研究,我發現:
- 模板是隱式外部的,但這確實會導致 ODR 違規
- 沒有任何 static 或 inilne 或 constexpr 裝飾,我最終會得到多個定義
- C 17 的關鍵字
inline改變了規則,但 c 20 有不同的行為(?)
那么是哪一個呢?
uj5u.com熱心網友回復:
模板從單一定義規則[basic.def.odr]/13中獲得例外:
如果每個定義出現在不同的翻譯單元中并且定義滿足以下要求,則程式中可以有多個 [...] 模板化物體 ([temp.pre]) [...] 的定義。
那里有很多要求,但基本上如果您在標頭中有一個變數模板(變數模板是一種模板化物體)并且只包含來自多個翻譯單元的該標頭,它們將具有相同的token-for-token相同定義(因為#include),所以有多個定義就可以了。
這與您不需要inline在標頭中宣告函式模板但確實需要宣告常規函式的原因相同inline。
在 C 17 中,這個措辭如下:
型別別、列舉型別、帶有外部鏈接的行內函式 ([dcl.inline])、帶有外部鏈接的行內變數 ([dcl.inline])、類模板、非靜態函式模板、如果每個定義都出現在不同的翻譯單元,并提供定義滿足以下要求。
請注意,變數模板不在該串列中,這只是一個遺漏(它總是打算作業)。這是CWG 2433,于 2019 年采用,但作為缺陷報告 (DR),因此我不會將其視為 C 20 更改。這是引入了我之前參考的“模板化物體”專案符號的 DR(而不是手動列出幾種不同型別的模板化物體,并且缺少一個)。
uj5u.com熱心網友回復:
這個變數使用安全嗎?
是的,您可以在標題中以這種方式宣告變數模板,然后在其他翻譯單元中使用它們。下面給出一個例子:
頭檔案.hpp
#ifndef HEADER_H
#define HEADER_H
#include <map>
template <class T>
std::map<T, std::string> errorCodes{}; //zero initialized value. Also, note there is no need to use keyword inline here
#endif
列印檔案
#ifndef PRINT_H
#define PRINT_H
void print();
#endif
列印.cpp
#include "print.hpp"
#include "header.hpp"
#include <iostream>
void print()
{
std::cout << errorCodes<int>.at(15) << std::endl; // This is safe: prints SomeString
}
主檔案
#include <iostream>
#include "print.hpp"
#include "header.hpp"
int main()
{
errorCodes<int>[15] = "SomeString"; //This is safe
print();
}
請注意,在上面的程式中,您不需要在定義變數 templateinline時使用header.hpp 中的關鍵字。C 編譯系統會處理這個問題。 errorCodes
上述程式的輸出可以在這里看到。
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/418486.html
標籤:
