c++ 模板參數有默認值時模板特例化匹配問題

如下的源碼:

template<typename T, typename U = int>
class S{ //#1
  public:
    void f1(){};
};

template<>
class S<void> {		//#2
  public:
    void f2(){};
};

int main()
{
  S<void, int> sv;	// OK: uses #2, definition available  #3
  //sv.f1();//error
  sv.f2();//ok
}

clang模板展開時,#3變成:

 S<void> sv = S<void>();

爲啥S<void, int> 不走主模板分支 #1?

其實 #2 的實現,S<void> 就是 S<void,int>。

實現過程:

class S<void> 的實例化,查找到前面有主模板定義:

template<typename T, typename U = int> class S

因爲第二個模板參數默認值是int所以 會自動生成

S<void, int>

也就是說,class S<void> 根據主模板,就是 S<void, int>。

接下來就是 S<void, int> 選擇哪個匹配問題。根據模板匹配找最適合的,範圍更小的。最終選定偏特化的,主模板的範圍太廣了。

再看一個小的示例:

//這個是主模板
template<bool, typename T= void> //這個T,可以省略,因爲沒有被引用
struct en_if {};

//一個偏特化模板。之所以不是全特化,還保留了主模板的第二個參數
template< typename T>
struct en_if <true, T> { using TYPE = T; };

/////------
en_if<true>::TYPE* a;

en_if<true>,這個根據主模板,形成: en_if<true, void>。(會去主模板,沒提供 T,默認是void,最終 T就變成了 void)。

接下來 en_if<true, void>選擇主模板,還是偏特化的問題。

主模板:template<bool,typename T=void>

偏特化:en_if<true, T>

偏特化更符合,範圍更小。即符合偏特化,肯定符合主模板;符合主模板,不一定符合偏特化。

偏特化被改的話,都會報錯:

//一個偏特化模板。之所以不是全特化,還保留了主模板的第二個參數
template< typename T=void>
struct en_if <true, T> { using TYPE = T; };

//一個偏特化模板。之所以不是全特化,還保留了主模板的第二個參數
template< typename T=int>
struct en_if <true, T> { using TYPE = T; };

上面兩種修改都會導致報錯:

error: default template argument in a class template partial specialization
template< typename T=void>
^
1 error generated.
 

實際應用

通過默認模板參數 來選擇默認的特化版本,當條件不成立時,退回到主模板。
  • 當默認模板參數是類型,則一般爲void,並在特化版本通過void_t或者 enable_if_t選擇;
  • 若爲值,則用bool常量表達式結果作爲默認參數並在特化版本中通過true false選擇。

以上摘自《c++20高級編程》羅能 p64.

enable_if 作爲約束使用,它雖然出現在模板上的一個參數,但是沒有用,只是作爲模板模式匹配用途。在c++20用約束來替換。

1, 關於默認值的模式匹配

template<bool B, class T=long>
struct enable_if{};

template<class T>
struct enable_if<true,T>{
  using type = T;
};

enable_if<true>::type a;

可以看到,a是個 long 類型。

template<bool B, class T = long>
struct enable_if
{
};

/* First instantiated from: insights.cpp:9 */
#ifdef INSIGHTS_USE_TEMPLATE
template<>
struct enable_if<true, long>
{
  using type = long;
};

#endif


template<class T>
struct enable_if<true, T>
{
  using type = T;
};

enable_if<true>::type a;

 2,它真正用途是作爲 選擇器。上面的long其實沒用,用void代替。

#include <iostream>

template<bool B, class T=void>
struct enable_if{};

template<class T>
struct enable_if<true,T>{
  using type = T;
};

template<class T, class U=typename enable_if< std::is_integral_v<T> >::type >
struct X{
};

X<int> ok;
X<std::string> compile_failed;

由於enable_if<false> 會匹配主模板定義,而裏面是沒有 type 這個定義的。所以導致 compile_failed。

可以看到上面的 class U模板參數只是作爲約束存在,沒有任何實際意義。c++20用 concept。

#include <iostream>

template<class T>
  concept IntType= std::is_integral_v<T>;


template<IntType T>
struct X{
};

X<int> ok;
//X<std::string> compile_failed;

進一步可以簡化爲:

template<class T>
requires std::is_integral_v<T>
struct X{
};

X<int> ok;

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章