模板 class 的模板模板参数对嵌套模板 class 的特化

specialization of template template paremeter of template class for nested template class

通常您可以针对实例化模板 class 部分特化模板 class。例如

template<class T>
struct specialize_me {};

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

模板 class specialize_me 部分专门针对实例化模板 class std::vector<T>。当使用 std::vector<T> 实例化 specialize_me 时,对于任何 class T.

,此特化是 selected
int main() {
    std::cout << specialize_me<std::vector<int>>::foo; // Compiles.
}

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

专门化模板模板 class
// 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;
};

在这种情况下,当我用 class Either<T>::template Or 实例化 specialize_me_2 时,特化是 而不是 selected任何 class T。我的猜测是,发生这种情况是因为编译器必须确认或拒绝 "There exists a T such that Either<T>::template Or is the same type as the specialize_me_2 instantiation" 才能 select 我的专业化,而且它没有被编程也没有指定这样做。

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,以便在 specialize_me_2 为任何 TEither<T>::Or 实例化时 selected 专门化?

这个Either结构最终将表示一个错误类型,所以Either<T>表示T是错误类型,Either<T>::Or<U>表示U是计算成功携带的类型。

如果这不可能,我可能仍然可以使用 #defines 使您能够根据需要为每个 T 定义 Either<T>,其中 #define 还包括针对特定 Either<T>::Orspecialize_me_2 专业化。事实上,我打算通过编写 template<class T> using FooError = Either<Foo>::Or<T> 在程序中使用 Either 结构,然后编写 FooError<Bar>FooError<Quux> 等等,所以使用它不会是一个巨大的偏离预期用途。

看起来 T 无法从 Either<T>::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

或者直接针对第一个模板参数专注于 Either,

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

有趣的问题。

轻松解决它...如果您可以在 Or

中添加新的 using 类型
template <typename T>
struct Either
 {
   template <typename>
   struct Or
    { using specialOrType = T; };
 };

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

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<void> 是一个有效的专业化。如果不是这种情况,您应该选择另一种类型 X,以便 Or<X> 是所有 Either<T> 的有效特化。例如,假设 Or<int> 是每个 Either<T> 的有效特化,特化变为

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;
 }