[英]Are reference non-type template parameters deduced by decltype(auto) forwardable in case of template template parameter
[英]Compiler variance for the type of deduced (non-type) template parameter
考慮以下示例:
#include <type_traits>
struct A {};
template <A const i> void f() {
static_assert(std::is_same_v<decltype(i), A const>); // #1
A& ar = i; // #2
}
int main() {
f<A{}>();
}
Clang (1)和 GCC (1)都拒絕了以下兩個看似沖突的錯誤:
#1 error: static_assert failed due to requirement 'std::is_same_v<A, const A>' #2 error: binding reference of type 'A' to value of type 'const A' drops 'const' qualifier
此外,如果我們將非類型模板參數的類型更改為占位符類型,如下所示:
template <auto const i> void g() {
static_assert(std::is_same_v<decltype(i), A const>); // #1
A& ar = i; // #2
}
然后 GCC 接受#1
,而 Clang 拒絕它(都拒絕#2
如上所述)。
這里發生了什么,哪個編譯器是正確的(如果有的話)?
(1) GCC HEAD 11.0.0 20210117 和 Clang HEAD 12.0.0 (20210118),-std -std=c++20
。
GCC 和 Clang 都(可能)正確地發出f
的#1
和#2
錯誤。
根據[temp.param]/6 [強調我的]:
非類型模板參數應具有以下類型之一(可能是 cv 限定的):
- (6.1)結構類型(見下文),
- (6.2) 包含占位符類型 ([dcl.spec.auto]) 的類型,或
- (6.3) 推導的 class 類型的占位符([dcl.type.class.deduct])。
在確定其類型時,模板參數上的頂級 cv 限定符將被忽略。
頂級 cv 限定符在確定其類型時被忽略; 與處理需要推導的占位符類型的 (6.2) 和 (6.3) 相比,對於 (6.1) 的“確定”所指的內容可以說有些模糊。
[dcl.type.decltype]/1 [強調我的]:
對於表達式
E
,由decltype(E)
表示的類型定義如下:
- [...]
- 否則,如果
E
是命名非類型模板參數的無括號 id 表達式,則decltype(E)
是執行任何必要類型推導后的模板參數的類型([dcl.spec.auto], [dcl.type .class.deduct]);- [...]
提到f
中的decltype(i)
的類型是非類型模板參數的類型,在執行任何必要的類型推導之后,后跟兩個連接到 [temp.param]/6.2 和 [temp.param]/ 的引用6.3.
因此, f
中#1
的關鍵是 [temp.param]/6.1 是否也經歷“確定其類型”,即使它被明確注釋為A const
(不需要推導)。 GCC 和 Clang 似乎都同意這種情況: A const
經歷“類型確定”並且const
被刪除。
理解f
中的#2
更直接; 與非類型模板參數(class類型) A
關聯的實際模板參數object不一定與非類型模板參數類型相同; 相反,它的類型由[temp.param]/8 [強調我的]:
An id-expression naming a non-type template-parameter of class type
T
denotes a static storage duration object of typeconst T
, known as a template parameter object , whose value is that of the corresponding template argument after it has been converted to the模板參數的類型。 相同類型程序中的所有此類模板參數具有相同的值表示相同的模板參數object。 模板參數 object 應具有常量破壞 ([expr.const])。 [注意:如果一個 id 表達式命名一個非類型非引用模板參數,那么如果它具有非類類型,它就是一個純右值。 否則,如果它是 class 類型 T,它是一個左值並且具有類型 const T ([expr.prim.id.unqual])。 ——尾注]
這樣任何 class 類型T
的模板參數 object始終具有類型const T
,從而解釋了#2
處的錯誤。 請注意,后者僅適用於 class 類型,因為命名非類型非引用模板參數的 id 表達式是純右值。
至於 GCC 和 Clang 在#1
in g
之間的差異,在應用 [temp.param]/6,特別是 [temp.param]/6.2 時沒有模糊性,在忽略 cv-qualifiers 的情況下應用類型推導范圍。 含義decltype(i)
是A
in g
,並且 GCC 接受#1
行是錯誤的。 我已相應地提交了以下 GCC 錯誤報告:
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.