【问题标题】:GCC/CLang disagree on partial specialization of template template parameterGCC/CLang 不同意模板模板参数的部分特化
【发布时间】:2018-10-09 15:57:13
【问题描述】:

GCC 和 clang 不同意这段代码。

#include <type_traits>

template <typename T, template <typename...> typename Tpl> 
struct storage {
    using type_t = T;

    template <typename... Args>
    using storage_tpl = Tpl<Args...>;
};

template <typename T, template <typename...> typename>
struct F{
    constexpr static int x = 1;
};

template <typename T >
struct F<T, std::void_t>{
    constexpr static int x = 2;
};

int f() {
    using S = storage<int, std::void_t>;

    static_assert(F<int, S::storage_tpl>().x == 2);

    return F<int, S::storage_tpl>().x;
}

根据clangS::storage_tpl不是std::void_t;结果,它选择了主模板 F 而不是部分特化,因此选择了断言。

乍一看,GCC 似乎是对的,因为它理解嵌套模板只是 std::void_t 的别名,但也许它太聪明了,标准要求 S::storage_tplstd::void_t 必须是两个不同的模板。

谁是对的?

【问题讨论】:

  • 这不是这种情况的正确参考,而是en.cppreference.com/w/cpp/language/type_alias中使用的措辞“别名模板是一个模板,当专门化时,它等效于替换别名模板的模板参数的结果对于 type-id 中的模板参数”意味着 storage_tpl 是独立的模板,其行为与 std::void_t
  • 有趣。如果您想稍微简化一点,您可以从storageF 中删除typename T 参数(显然也删除type_t)和F。 g++ 和 clang++ 的分歧依然存在。
  • 在[temp.class.spec.match]/2中说如果部分特化的模板参数可以是来自实际模板参数列表的deduced,其中 deduced 是指向 [temp.deduct] 的链接,其中...从模板参数中扣除模板参数没有任何意义(这是有道理的,因为 [temp.deduct] 部分在 function template specialization 之下.我认为该标准未充分说明。此外,在 Template 下的标准中有一个称为 Type Equivalence 的部分,但没有针对 Template Equivalence的规范>.
  • 别名模板结合模板模板参数是非推导上下文,基本上你最终会遇到与此线程stackoverflow.com/questions/52462410/…中相同的问题(部分排序和非推导上下文)。

标签: c++ gcc language-lawyer clang++ template-specialization


【解决方案1】:

目前看来这是未指定的,h/t to T.C. 似乎已被 CWG defect report 1286 覆盖,上面写着:

通过更改 17.5 [temp.type] 中的示例解决了问题 1244 第 1 段来自

 template<template<class> class TT> struct X { };
  template<class> struct Y { };
  template<class T> using Z = Y<T>;
  X<Y> y;
  X<Z> z;

  template<class T> struct X { };
  template<class> struct Y { };
  template<class T> using Z = Y<T>;
  X<Y<int> > y;
  X<Z<int> > z;

事实上,原本的意图是该示例应该是 书面正确;然而,这样做的规范措辞是 失踪。 17.6.7 [temp.alias] 的当前措辞仅涉及 别名模板的特化与 替换后的类型 ID。需要添加措辞,具体说明如下 什么情况下别名模板本身就相当于一个类 模板。

并提出以下决议:

在 17.6.7 [temp.alias] 之后添加以下内容作为新段落 第 2 段:

当别名模板(称为 A)声明中的 type-id 由一个 simple-template-id 组成时,其中 template-argument-list 由命名 A 的每个模板参数的标识符列表组成 以它们出现在 A 中的相同顺序恰好出现一次 template-parameter-list,别名模板相当于 以 simple-template-id 命名的模板(称为 T),如果 A 和 T 有 相同数量的模板参数。 [脚注:这条规则是 传递性:如果一个别名模板 A 等价于另一个别名 等价于类模板C的模板B,那么A也是 等价于C,A和B也等价于彼此。 -结尾 脚注] [示例:

  template<typename T, U = T> struct A;

  template<typename V, typename W>
    using B = A<V, W>;                // equivalent to A

  template<typename V, typename W>
    using C = A<V>;                   // not equivalent to A:
                                      // not all parameters used

  template<typename V>
    using D = A<V>;                   // not equivalent to A:
                                      // different number of parameters

  template<typename V, typename W>
    using E = A<W, V>;                // not equivalent to A:
                                      // template-arguments in wrong order

  template<typename V, typename W = int>
    using F = A<V, W>;                // equivalent to A:
                                      // default arguments not considered

  template<typename V, typename W>
    using G = A<V, W>;                // equivalent to A and B

  template<typename V, typename W>
    using H = E<V, W>;                // equivalent to E

  template<typename V, typename W>
    using I = A<V, typename W::type>; // not equivalent to A:
                                      // argument not identifier

——结束示例]

但此解决方案存在问题,并且缺陷报告仍然有效。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-10-27
    • 2019-01-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多