簡體   English   中英

static constexpr模板成員在專門化時給出undefined-reference

[英]static constexpr template member gives undefined-reference when specialized

以下代碼給出了未定義的引用鏈接錯誤:

template<int>
struct X {
    static constexpr int x = 0;
};

template<>
constexpr int X<1>::x;

int main() 
{   
    return X<1>::x;
}

但我不確切知道為什么。

是否可以定義數據成員而不專門化整個模板?

要明確:此代碼編譯良好,但提供鏈接器錯誤(未定義引用)。

由於明確專業化,你偶然發現了一個“小”問題。 如果我們引用[temp.expl.spec] / 13

如果聲明包含初始化程序,則模板的靜態數據成員的顯式特化或靜態數據成員模板的顯式特化是定義; 否則,這是一個聲明。 [注意:需要默認初始化的模板的靜態數據成員的定義必須使用braced-init-list:

 template<> X Q<int>::x; // declaration template<> X Q<int>::x (); // error: declares a function template<> X Q<int>::x { }; // definition 

- 結束說明]

這意味着您聲明 X<1>::x存在,但不能定義它。 因此它未定義。

我覺得很瘋狂的是你的編譯器只接受它。 通常,您無法在不定義constexpr變量的情況下聲明constexpr變量。 這很奇怪。

是否可以在沒有[專門化]整個模板的情況下定義數據成員?

static類模板的數據成員可以明確專門([temp.expl.spec]),但是如果你想做到這一點,你可以不指定任何一種用於初始化類模板中的成員(class.static.data )。 那是,

如果我們用const替換constexpr ,這個代碼就可以了:

template<int>
struct X {
    static const int x;
};

template<int I>
const int X<I>::x = 0;

template<>
const int X<1>::x = 1;

但是這段代碼不會很好:

template<int>
struct X {
    static const int x = 0;
};

template<>
const int X<1>::x = 1;

您可以看到區別在於我們初始化主模板的變量。

現在,如果我們希望用constexpr替換const ,那么我們需要提供一個初始化器(class.static.data):

可以使用constexpr說明符在類定義中聲明文字類型的static數據成員; 如果是這樣,它的聲明應該指定一個大括號或者相等的初始化器 ,其中作為賦值表達式的每個initializer子句都是一個常量表達式

因此,我們最終會遇到這種奇怪的情況,我們可以專門定義static成員,但如果是constexpr不會因為constexpr需要初始化程序。 恕我直言,這是標准的缺點。

然而,似乎所有現代編譯器都不同意。

gcc 8.0.0按原樣編譯(但不鏈接)你的代碼(錯誤),但是如果為專門化添加初始化程序,它會抱怨重復初始化(右)。

clang 6.0.0不會按原樣(右)編譯代碼,但是當你添加初始化程序時,它可以毫無障礙地工作(錯誤,但這可能是標准應該指定的)

MSVC 19.00.23506不按原樣編譯代碼(右),並且在添加初始化程序時沒有編譯代碼(抱怨重定義)(右)。

最后,將專業化推入輔助Traits類可能更容易:

template<int>
struct X_Traits{
    static constexpr int value = 0;
};

template<>
struct X_Traits<1>{
    static constexpr int value = 1;
};

template<int I>
struct X {
    static constexpr int x=X_Traits<I>::value;
    // ...
};

在C ++ 17及更高版本中,我們可以使用constexpr,以避免需要專門化我們的traits類:

template<int I>
struct X_Traits{
    static constexpr int get_value(){
        if constexpr(I==1){
            return 1;
        }else{
            return 0;
        }
    }
};

template<int I>
struct X {
    static constexpr int x=X_Traits<I>::get_value();
    // ...
};

int main(){
    static_assert(X<0>::x == 0);
    static_assert(X<1>::x == 1);
}

像這樣。

template<int i>
struct X {
    static constexpr int x = i==0?2:10;
};

int main() 
{   
    return X<1>::x;
}

請注意,您不需要在課外定義它。

暫無
暫無

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

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