【问题标题】:Multiple SFINAE class template specialisations using void_t使用 void_t 的多个 SFINAE 类模板特化
【发布时间】:2017-08-29 22:05:28
【问题描述】:

当每个类模板特化仅在涉及非推导上下文中的模板参数的模式之间是不同的时,多个类模板特化是否有效?

std::void_t 的一个常见示例使用它来定义一个特征,该特征揭示一个类型是否有一个名为“type”的成员 typedef。在这里,采用了单一的专业化。这可以扩展来识别一个类型是否有或者一个名为“type1”的成员typedef,或者一个名为“type2”的成员。下面的 C++1z 代码使用 GCC 编译,但不是 Clang。合法吗?

template <class, class = std::void_t<>>
struct has_members : std::false_type {};

template <class T>                      
struct has_members<T, std::void_t<typename T::type1>> : std::true_type {};

template <class T>                                                        
struct has_members<T, std::void_t<typename T::type2>> : std::true_type {};

【问题讨论】:

  • 暂时看不出有什么问题。 clang 的错误信息到底是什么意思?
  • 对于同时具有type1type2 的类不合适吗?
  • 离题了,但是写bool_constant&lt;true&gt; 而不是更短的true_type 很奇怪(false 也是如此)
  • 这取决于using 何时必须被理解——何时声明,或者何时发生模板替换。当声明发生时,Clang 正在摸索它(似乎很明显这就是它正在做的事情)。这对我来说显然是错误的。我认为 clang 是错误的,在这里。

标签: c++ c++17 template-specialization sfinae partial-specialization


【解决方案1】:

有一条规则,部分特化必须比主模板更特化 - 您的两个特化都遵循该规则。但是没有一条规则规定部分专业化永远不会模棱两可。更重要的是 - 如果实例化导致模棱两可的专业化,则程序格式错误。但必须首先发生这种模棱两可的实例化!

clang 似乎在这里受到CWG 1558 的困扰,并且过于渴望用void 替换std::void_t

这几乎是CWG 1980

举个例子

template<typename T, typename U> using X = T;
template<typename T> X<void, typename T::type> f();
template<typename T> X<void, typename T::other> f();

看来f 的第二个声明是对第一个但可被 SFINAE 区分的重新声明,即等效但在功能上不等效。

如果你使用void_t的非别名实现:

template <class... Ts> struct make_void { using type = void; };
template <class... Ts> using void_t = typename make_void<Ts...>::type;

然后 clang 允许两种不同的专业化。当然,在同时具有 type1type2 typedefs 错误的类型上实例化 has_members,但这是意料之中的。

【讨论】:

  • 1558 只处理是否完全应用替换,而不是声明匹配,这是一个完全不同的野兽。这更接近 CWG 1980。别名模板透明但不完全透明的整个业务被证明是棘手的 :)
  • @T.C.呵呵,正在寻找包含“专业化”的问题。过滤器不好。谢谢!
【解决方案2】:

我不认为这是正确的,或者至少,如果我们使用嵌套了 type1 和 type2 的类型实例化 has_members,结果将是两个特化

has_members<T, void> 

这将是无效的。在代码被实例化之前,我认为没关系,但是 clang 很早就拒绝了它。在 g++ 上,一旦实例化,您将在此用例中失败:

struct X
{
    using type1 = int;
    using type2 = double;
};

int main() {
    has_members<X>::value;
}

错误消息似乎没有描述实际问题,但至少发出了:

<source>:20:21: error: incomplete type 'has_members<X>' used in nested name specifier
     has_members<X>::value;
                     ^~~~~

如果你用只有 type1 type2 但不是两者都有的类型实例化它, 然后 g++ 干净地编译它。因此,它反对成员都存在这一事实,从而导致模板的实例化冲突。

为了得到析取,我想你需要这样的代码:

template <class, class = std::void_t<>>
struct has_members : std::bool_constant<false> {};

template <class T>
struct has_members<T, std::enable_if_t<
        std::disjunction<has_member_type1<T>, has_member_type2<T>>::value>> : 
    std::bool_constant<true> {};

这假设你有特征来确定 has_member_type1 和 has_member_type2 已经写好了。

【讨论】:

  • 我知道这对包含两个成员的类型无效。该代码只是帮助阐明主要问题的示例。
猜你喜欢
  • 1970-01-01
  • 2021-06-05
  • 2012-10-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多