[英]CRTP with Template Template Arguments
以下代碼無法編譯......
namespace {
template<typename T, template<typename> class D>
struct Base {
Base(const T& _t) : t(_t) { }
T t;
};
template<typename T>
struct Derived : Base<T, Derived> {
Derived(const T& _t) : Base<T, Derived>(_t) { }
};
}
int main(int argc, char* argv[]) {
Derived<int> d(1);
return 0;
}
該行有一個編譯錯誤 - Derived(const T& _t) : Base<T, Derived>(_t) { }
錯誤C3200''anonymous-namespace':: Derived':模板參數'D'的模板參數無效,需要一個類模板
如果我提供任何其他具有模板參數而不是Derived本身的類,則此方法有效
template<typename T>
struct Other {
};
template<typename T>
struct Derived : Base<T, Other> {
Derived(const T& _t) : Base<T, Other>(_t) { }
};
Tl; dr:解決該問題的最便攜和最不廣泛的方法似乎是在您的示例中使用限定名稱::Derived
:
template<typename T>
struct Derived : Base<T, Derived>
{
Derived(const T& _t) : Base<T, ::Derived>(_t) { }
};
問題是編譯器不符合C ++ 11。
與普通(非模板)類一樣,類模板具有注入類名(第9節)。 注入的類名可以用作模板名或類型名。 當它與template-argument-list一起使用時, 作為模板模板參數的模板參數 ,或作為友元類模板聲明的詳細說明類型說明符中的最終標識符, 它引用類模板本身。
因此,您的代碼應該編譯,但遺憾的是,我測試的所有編譯器(clang 3.7,Visual Studio 2015和g ++ 5.3)都拒絕這樣做。
Afaik,你應該能夠在Derived
定義中以各種方式表示模板:
Derived
::Derived
Derived<T>::template Derived
::template Derived
這些編譯器關於這四個選項的編譯狀態如下(使用anonymus命名空間;其中+
=成功編譯):
+------------------------------+----------+---------+-----------+
| Method | MSVS2015 | g++ 5.3 | clang 3.7 |
+------------------------------+----------+---------+-----------+
| Derived | - | - | - |
| ::Derived | + | + | + |
| Derived<T>::template Derived | - | - | + |
| ::template Derived | + | - | + |
+------------------------------+----------+---------+-----------+
當給名稱空間命名為X
,圖片會稍微改變(即g++
現在接受X::template Derived
而它拒絕::template Derived
):
+---------------------------------+----------+---------+-----------+
| Method | MSVS2015 | g++ 5.3 | clang 3.7 |
+---------------------------------+----------+---------+-----------+
| Derived | - | - | - |
| X::Derived | + | + | + |
| X::Derived<T>::template Derived | - | - | + |
| X::template Derived | + | + | + |
+---------------------------------+----------+---------+-----------+
在類模板中,inject-class-name(在您的示例中為Derived
)可以是類型名稱和模板名稱。 該標准指定在用作模板模板參數的參數時應該考慮將模板命名(因此您的代碼應該起作用),但遺憾的是一些編譯器尚未實現此功能。
一種解決方法是使用限定名稱,這樣您就不會使用inject-class-name,而是直接命名模板:
template<typename T>
struct Derived : Base<T, Derived> {
Derived(const T& _t) : Base<T, ::Derived>(_t) { }
};
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.