【问题标题】:Is it legal to partially specialise variadic template inner class with args from variadic template of an outer class使用来自外部类的可变参数模板的 args 部分专门化可变参数模板内部类是否合法
【发布时间】:2016-06-11 19:06:26
【问题描述】:

考虑代码:

#include <iostream>

template <class... Ts>
struct outer {
   template <class... ITs>
   struct inner {
      static constexpr bool value = false;
   };

   template <class... ITs>
   struct inner<Ts..., ITs...> {   
      static constexpr bool value = true;
   };
};

int main() {
   std::cout << outer<int, float, double>::inner<int, float, double, int>::value << std::endl;
}

代码用 clang++ 编译,但不能用 g++ 编译,它会产生错误:

temp3.cc:11:11:错误:参数包参数“Ts ...”必须位于 模板参数列表的结尾

struct inner<Ts..., ITs...> {
       ^

正如我已经建立的here 内部类的部分专业化应该是合法的。

编辑: 为了完整起见,值得补充的是,上述代码的 clang 警告他在推导 IT 参数时可能会遇到问题,但没有任何问题......

【问题讨论】:

  • 我并不确切地知道规则,但是当我遇到依赖类型的错误时,在它之前添加 typenametemplate 有时会有所帮助。试试struct inner&lt;typename Ts...
  • 添加typename后我得到temp3.cc:11:39: error: template argument 1 is invalid
  • 需要注意的是,请求的场景仍然可以通过一些额外的模板元编程来实现...coliru.stacked-crooked.com/a/0c6c643c8ff5809e(是的,我知道这不是问题,但实现它的挑战是不可避免的...)。

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


【解决方案1】:

这是一个 gcc 错误。这是一个完全有效的偏特化:

template <class... ITs>
struct inner<Ts..., ITs...> {   
   static constexpr bool value = true;
};

推导的模板参数包必须在最后,ITs... 满足这一点。但是Ts...不是这里需要推导的包,只是具体的参数包。

此外,gcc 编译了几个等效的公式:

template <class... Ts>
struct X {
    template <class... Us>
    static void foo(Ts..., Us...) { }
};

int main() {
   X<int>::foo(1, 'c');
}

和:

template <class... Us>
struct A { };

template <class... Ts>
struct X {
    template <class... Us>
    static void foo(A<Ts..., Us...>) { }
};

int main() {
   X<int>::foo(A<int, char>{});
}

这些与原始示例的格式相同。

【讨论】:

  • 我怀疑但不确定模板类中的专业化上下文是否有一些特殊规则。这是一个已知的错误吗?
  • 我同意部分专业化有效是有意义的,但我不确定所写的标准是否允许它。 [14.5.5p8.5] 说如果一个参数是一个包扩展(14.5.3),它应该是模板参数列表中的最后一个参数。这是对部分专业化的特定要求,因此不适用于您的示例。在我看来,这更像是一个标准问题,而不是编译器错误。
  • 你应该向gcc提交错误报告。你做完了吗?
  • 我见过the discussion,但我不相信。我会回复那个帖子。
  • 我认为我们不会从该线程中获得更多信息。我根据讨论得出的结论是,这样做的目的是为了使这种方法起作用,但对措辞的严格解释是不允许的。 GCC、MSVC 和 EDG 采用字面意义的措辞,而 Clang 实现预期的行为;如果不首先澄清措辞,就很难将责任归咎于他们中的任何一个。我听说有一条 ISO 规则指定 std-discussion 上的 OP 也是发送问题报告的人:-)。
【解决方案2】:

根据 W.F. 的回答,仍然保持与原始问题相同的主要内容:

#include <iostream>

template <class... Ts>
struct pack { };

template <class... Ts>
class outer {
   template <class IT>
   struct _inner {
      static constexpr bool value = false;
   };

   template <class... ITs>
   struct _inner<pack<Ts..., ITs...>> {
      static constexpr bool value = true;
   };
public:
   template <class... ITs>
   struct inner {
      static constexpr bool value = _inner<pack<ITs...>>::value;
   };    
};

int main() {
   std::cout << outer<int, float, double>::inner<int, float, double, int>::value
             << std::endl;
}

它仍然会在 clang 中产生警告,因为 _inner 的专用版本无法推断出 IT...除了 Ts...、ITs... 的列表(在 struct _inner&lt;pack&lt;Ts..., ITs...&gt;&gt; 中)- 但是代码不需要从 Ts...、ITs...的列表中单独推导出 ITs,所以应该可以。

在 g++ 中编译时没有警告。

代码:http://coliru.stacked-crooked.com/a/ae3b21dd847450b2

(对于没有警告的解决方案也在clang中:http://coliru.stacked-crooked.com/a/0c6c643c8ff5809e)。

【讨论】:

    【解决方案3】:

    受 Barry 回答启发的可能简单而有效的解决方法:

    #include <iostream>
    
    template <class... Ts>
    struct pack { };
    
    template <class... Ts>
    struct outer {
       template <class IT>
       struct inner {
          static constexpr bool value = false;
       };
    
       template <class... ITs>
       struct inner<pack<Ts..., ITs...>> {   
          static constexpr bool value = true;
       };
    };
    
    int main() {
       std::cout << outer<int, float, double>::inner<pack<int, float, double, int>>::value << std::endl;
    }
    

    (但它仍然会在 clang 中产生警告)

    【讨论】:

      猜你喜欢
      • 2016-10-12
      • 1970-01-01
      • 2018-07-31
      • 1970-01-01
      • 2017-08-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多