嵌套模板类

时间:2018-06-17 08:30:22

标签: c++ templates template-specialization

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

template<class T>
struct specialize_me {};

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

模板类specialize_me部分专门针对实例化的模板类std::vector<T>。对于任何类specialize_mestd::vector<T>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;
};

在这种情况下,当我使用类specialize_me_2为任何类Either<T>::template Or实例化T时,专业化是 not 。我的猜测是,这是因为编译器必须确认或拒绝,&#34;存在TEither<T>::template Orspecialize_me_2实例化的类型相同&#34;为了选择我的专业化,它没有编程也没有指定这样做。

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 Either<T>::OrT Either实例化,就会选择专精化?

Either<T>结构最终将表示携带错误的类型,因此T表示Either<T>::Or<U>是错误类型,U表示#define是成功计算所携带的类型。

如果这是不可能的,我可能仍然可以使用Either<T>来为每个T定义#define,因为specialize_me_2也是Either<T>::Or包括该特定Either的{​​{1}}专精。实际上,我打算在程序中使用template<class T> using FooError = Either<Foo>::Or<T>结构,然后写FooError<Bar>,然后写FooError<Quux> const mergeStrings = (...strings) => strings.join(" "); 等等,所以使用它不会与预期用途的巨大突破。

2 个答案:

答案 0 :(得分:1)

有趣的问题。

要解决它而不会太痛苦...如果您可以在using内添加新的Or类型

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

并使用SFINAE而不是specialOrType

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

答案 1 :(得分:0)

Either<T>::Or的专业化中无法推断T,传递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