【问题标题】:specialization of template template paremeter of template class for nested template class嵌套模板类的模板类模板参数特化
【发布时间】:2018-11-26 10:40:42
【问题描述】:

通常,您可以针对实例化的模板类部分专门化模板类。比如

template<class T>
struct specialize_me {};

template<class T>
struct specialize_me<std::vector<T>> {
    static const int foo = 3;
};

模板类specialize_me 部分专门针对实例化的模板类std::vector&lt;T&gt;。当specialize_mestd::vector&lt;T&gt; 实例化时,会选择此特化,适用于任何类T

int main() {
    std::cout << specialize_me<std::vector<int>>::foo; // Compiles.
}

但是,我不知道如何针对实例化的嵌套模板类专门化模板模板类:

// Nested template class.
template<class T>
struct Either {
    template<class U>
    struct Or {};
};

template<template<class> class T>
struct specialize_me_2 {};

template<class T>
struct specialize_me_2<Either<T>::template Or> {
    static const int foo = 3;
};

在这种情况下,当我为任何类T 使用类Either&lt;T&gt;::template Or 实例化specialize_me_2 时,选择特化。我的猜测是,发生这种情况是因为编译器必须确认或否认,“存在一个 T 使得 Either&lt;T&gt;::template Orspecialize_me_2 实例化的类型相同”才能选择我的专业化,而事实并非如此编程或指定这样做。

int main() {
    std::cout << specialize_me_2<Either<int>::Or>::foo; // Does not compile.  'foo' is not a member of specialize_me_2<Either<int>::Or>.
}

有没有办法专门化specialize_me_2,以便在任何TEither&lt;T&gt;::Or 实例化specialize_me_2 时选择专门化?

这个Either结构体最终会代表一个携带错误的类型,所以Either&lt;T&gt;表示T是错误类型,Either&lt;T&gt;::Or&lt;U&gt;表示U是成功携带的类型计算。

如果这是不可能的,我可能仍然可以使用#defines 让您根据需要为每个T 定义Either&lt;T&gt;,其中#define 还包括specialize_me_2 专门化特别是Either&lt;T&gt;::Or。事实上,我打算在程序中使用Either 结构,无论如何都要写template&lt;class T&gt; using FooError = Either&lt;Foo&gt;::Or&lt;T&gt;,然后写FooError&lt;Bar&gt;FooError&lt;Quux&gt; 等等,所以使用它不会与预期的用法有很大的不同。

【问题讨论】:

  • 我不明白这个。在您的第二个示例中,Or 应该用什么实例化?
  • 模板参数类型推导仅适用于::的右侧,因此编译器无法在Either&lt;T&gt;::template Or中推导T

标签: c++ templates template-specialization


【解决方案1】:

有趣的问题。

为了解决它而不会太痛苦......如果你可以在Or中添加新的using类型

template <typename T>
struct Either
 {
   template <typename>
   struct Or
    { using specialOrType = T; };
 };

然后您可以在specialize_me_2 中添加第二个模板参数,即默认为void 的类型名

template <template <typename> class C, typename = void>
struct specialize_me_2
 { static const int foo = 2; };

specialOrType上使用SFINAE

template <typename ...>
using myVoidT = void;

template <template <typename> class C>
struct specialize_me_2<C, myVoidT<typename C<void>::specialOrType>>
 {
   using T = typename C<void>::specialOrType;

   static const int foo = 3;
 };

你获得了你的工作专长。

代替myVoidT,从C++17开始,你显然可以使用std::void_t

注意,这种方式无法推断出原来的T类型,但可以通过specialOrType恢复。

还请注意,这要求(如 aschepler 所指出的)Or&lt;void&gt; 是有效的特化。如果不是这种情况,您应该选择另一种类型X,以便Or&lt;X&gt; 是所有Either&lt;T&gt; 的有效特化。例如,假设Or&lt;int&gt; 是每个Either&lt;T&gt; 的有效特化,则特化变为

template <template <typename> class C>
struct specialize_me_2<C, myVoidT<typename C<int>::specialOrType>>
 {
   using T = typename C<int>::specialOrType;

   static const int foo = 3;
 };

以下是一个完整的工作示例

#include <iostream>

template <typename ...>
using myVoidT = void;

template <typename>
struct NoEither
 { };

template <typename T>
struct Either
 {
   template <typename>
   struct Or
    { using specialOrType = T; };
 };

template <template <typename> class C, typename = void>
struct specialize_me_2
 { static const int foo = 2; };

template <template <typename> class C>
struct specialize_me_2<C, myVoidT<typename C<void>::specialOrType>>
 {
   using T = typename C<void>::specialOrType;

   static const int foo = 3;
 };

int main ()
 {
    std::cout << specialize_me_2<NoEither>::foo << std::endl;
    std::cout << specialize_me_2<Either<int>::template Or>::foo << std::endl;
 }

【讨论】:

  • 不错,但它要求 Or&lt;void&gt; 是一个有效的专业化。
  • @aschepler - 是的......可能是个问题......但我希望存在一个类型X,以便Or&lt;X&gt;是一个有效的专业化......嗯......也许是在答案中明确这一点的案例。
  • 可惜你不能专攻template &lt;typename T&gt; template&lt;&gt; struct Either&lt;T&gt;::Or&lt;dummy_tag&gt;
  • 严格来说,specialize_me_2 的特化是根据什么规则为实例化specialize_me_2&lt;Either&lt;int&gt;::template Or&gt; 选择的? AFAICT, SFINAE 适用于所有类型 除了 Either&lt;T&gt;::template Or。对于那种类型,基本模板是一个完美的匹配。重载集是如何排序的?
  • @jcarpenter2 - 两个模板都匹配。但是专业化,准确地说,更加专业化。我的意思是:如果模板模板匹配专业化,肯定也匹配基本模板;但是如果模板模板与基本模板匹配,则不一定与特化匹配(参见NoEither 案例:匹配基,而不是特化)。因此,当模板模板也匹配特化时,编译器必须使用特化。我不知道是否清楚(对不起,我英语说得不太好)。
【解决方案2】:

看起来 T 无法从 Either&lt;T&gt;::Or 推导出来,将 T 作为另一个模板参数传入似乎可以使它工作,如果这对你也有用的话..

#include <iostream>

template<class T>
struct Either {
    template<class U>
    struct Or {};
};


template<typename U,template<class> class T>
struct specialize_me_2 {};

template<class T>
struct specialize_me_2<T,Either<T>::template Or> {
    static const int foo = 3;
};


int main() {
    std::cout << specialize_me_2<int,Either<int>::Or >::foo; // Does compile
}

Demo

使用Either 中的嵌套类型成员可以使其更简洁,因此您可以使用 like,

using someEitherType = Either<int>;
...
specialize_me_2<someEitherType::type, someEitherType::Or>::foo

或者直接专门针对第一个模板参数,

template<class T>
struct specialize_me_2<Either<T>,Either<T>::template Or> {
    static const int foo = 3;
};

然后传入

specialize_me_2<someEitherType, someEitherType::Or>::foo

Demo2

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-01-11
    • 2011-05-10
    • 2018-06-25
    • 2012-12-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多