【问题标题】:Partial specialization for variadic template needs first non-variadic template parameter可变参数模板的部分特化需要第一个非可变参数模板参数
【发布时间】:2023-03-13 19:12:02
【问题描述】:

以下代码

#include <iostream>
#include <utility>

template<typename F, typename... T>
struct Wrapper{ };

template<typename T>
struct is_wrapper : std::false_type {};

template<typename... T>
struct is_wrapper<Wrapper<T...>> : std::true_type {};

//template<typename F, typename... T>
//struct is_wrapper<Wrapper<F, T...>> : std::true_type {};

int main()
{
    Wrapper<int, double> w;

    std::cout << is_wrapper<decltype(w)>::value << std::endl;
}

打印 0。但是,如果取消中间的两行,它会打印 1。

为什么它不总是打印 1?第二部分专业化不应该也涵盖显然仅由第三(注释)部分专业化涵盖的情况吗?

【问题讨论】:

    标签: c++ c++11 variadic-templates partial-specialization


    【解决方案1】:

    代码确实应该匹配偏特化;该标准实际上从未不允许这样做,但编译器确实需要一段时间来正确实现可变参数模板及其推导。 GCC 自 4.9.0 起符合,Clang 自 3.6 起符合。 Clang 的相关错误报告是 #22191(不过我找不到 GCC)。

    【讨论】:

      【解决方案2】:

      通常,当我编写专门化的模板时,我会先使用前向声明,然后声明案例属于专门化。在您的情况下,我了解您正在尝试编写一个没有空大小写的可变参数模板(也就是说,一个可变参数模板至少可以有一种类型)。

      您的代码让我感到惊讶,因为我认为您是正确的,您的 trait 的完整可变参数特化与空洞情况相匹配...首先我尝试使用您的 trait 类的前向声明,并仅定义完整可变参数特化(因此,如果特征的参数不是 Wrapper 实例,则编译失败)。而这正是发生的事情,再次让我失望:

      #include <iostream>
      #include <utility>
      
      template<typename F , typename... T>
      struct Wrapper {};
      
      template<typename T>
      struct is_wrapper;
      
      //template<typename T>
      //struct is_wrapper : std::false_type {};
      
      template<typename... T>
      struct is_wrapper<Wrapper<T...>> : std::true_type {};
      
      //template<typename F, typename... T>
      //struct is_wrapper<Wrapper<F, T...>> : std::true_type {};
      
      using my_wrapper_type = Wrapper<int,double>;
      
      int main()
      {   
          std::cout << std::boolalpha  << is_wrapper<my_wrapper_type>::value << std::endl;
      }//"Invalid use of incomplete type" ^^^^^^^^^^^^^^^^^^^^^^^^^^^
      

      最后我尝试了Wrapper 类中的前向声明方法。令人惊讶的是,它确实有效:

      #include <iostream>
      #include <utility>
      
      template<typename... T>
      struct Wrapper;
      
      template<typename F, typename... T>
      struct Wrapper<F,T...>{ };
      
      
      template<typename T>
      struct is_wrapper : std::false_type {};
      
      template<typename... T>
      struct is_wrapper<Wrapper<T...>> : std::true_type {};
      
      //template<typename F, typename... T>
      //struct is_wrapper<Wrapper<F, T...>> : std::true_type {};
      
      
      using my_wrapper_type = Wrapper<int,double>;
      
      int main()
      {
          std::cout << std::boolalpha  << is_wrapper<my_wrapper_type>::value << std::endl;
      }
      

      打印出来:

      是的

      Here是ideone运行的代码。

      真诚地,我不明白为什么您的代码失败而我的代码有效。 这是一个编译器错误,还是我们遗漏了什么?我不知道。

      【讨论】:

      • 区别在于类模板Wrapper的模板参数个数。您的(第二个)版本只有一个模板参数,而 OP 的版本有两个参数。您的部分专业化 struct Wrapper&lt;F,T...&gt; 不会改变这一点。
      • @DyP 好的,但是如果您有一个带有两个参数的模板(一个普通模板参数和一个可变参数模板参数)并且您将可变参数包传递给该模板,则可变参数包不必扩展匹配第一个普通参数和第二个参数(可变参数)?这就是我不明白的地方,为什么不以那种方式扩展包。
      • 我仍在试图弄清楚标准在哪里(以及为什么)在这种情况下禁止部分专业化以匹配参数包。猜猜它在 [temp.deduct.type]/9 附近的某个地方。 (包展开不同于推导。)
      • @DyP 我也是 :) 这就是我的意思 “我不明白为什么你的代码失败而我的代码工作”
      【解决方案3】:

      如果我能很好地理解你的问题,这只是一个专业优先级,

      is_wrapper<decltype(w)>
      

      可以被2个模板特化:

      template<typename T> // exact specialization
      template<typename... T> // variadic specialization with one parameter 
      

      在这种情况下,编译器会优先选择确切的专业化,因此在您的情况下模板永远不会实例化。

      【讨论】:

      • 这是不正确的。部分特化总是优先于主模板(并且必须更特化,无论如何),请参阅 §14.5.5.1。
      猜你喜欢
      • 1970-01-01
      • 2012-01-14
      • 2016-12-01
      • 2017-10-15
      • 1970-01-01
      • 2023-03-07
      • 2018-07-31
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多