说,我有一些专门针对几种类型的模板TypeMathcer
,它具有type
成员。
#include <memory>
#include <vector>
template <typename T>
struct TypeMatcher;
template <typename T>
struct TypeMatcher<T *>
{
// making some type from T
typedef std::shared_ptr<T> type;
};
template <typename T>
struct TypeMatcher<T&>
{
// making other type from T
typedef std::vector<T> type;
};
现在,我想创建另一个模板,并将其专门用于从
TypeMatcher
获得的类型。如果我直接做到这一点template <typename T>
struct MyNeedfullTemplate;
template <typename T>
struct MyNeedfullTemplate<typename TypeMatcher<T>::type>
{
};
我收到编译器错误:
template parameters not deducible in partial specialization
。如果使用
using
语法,则出现相同的错误template <typename T>
using type_matcher_t = typename TypeMatcher<T>::type;
template <typename T>
struct MyNeedfullTemplate;
template <typename T>
struct MyNeedfullTemplate<type_matcher_t<T> >
{
};
我阅读的问题partial specialization for iterator type of a specified container type的答案与我的问题非常相似,但仍不确定是否存在一个反例使所有问题变得毫无意义。现在我们还拥有全新的c++ 14和c++ 17标准,这些标准可能会改变现状。那么,如果我确保特化是唯一且存在的,那么使参数可推论的可能性将最大吗?
最佳答案
从原则上讲,这是不可能的,没有任何花哨的C++ 9999可以改变它。
您要编译器执行的操作:
代码中有MyNeedfulTemplate<int>
这样的用法。编译器需要为MyNeedfulTemplate<U>
定义U = int
。您尝试提供表格的部分特化
template <typename T>
struct MyNeedfullTemplate<typename TypeMatcher<T>::type>
要查看该特化是否适用,编译器将必须检查
TypeMatcher<T>
中所有可能的T
和中的,并查找其中是否有嵌套的typedef type
作为int
的别名。这不可能发生,因为“所有可能的T
s”的集合是无限的。好的,TypeMatcher<int>
没有这种类型,TypeMatcher<int*>
,TypeMatcher<int**>
和TypeMatcher<int***>
也没有。但是,如果TypeMatcher<int****>
呢?最好继续尝试...还请记住存在部分和完全特化,这意味着
TypeMatcher
本身可以被特化。简而言之,如果您只有
int
而不是TypeMatcher<X>::type
,则无法将int
链接到X
。您应该可以通过稍微重构(反转)
TypeMatcher
来达到类似的目的:template <class T>
struct TypeMatcher2
{
static constexpr specialised = false;
};
template <class T>
struct TypeMatcher2<std::shared_ptr<T>>
{
static constexpr specialised = true;
using OldType = T*;
};
template <class T>
struct TypeMatcher2<std::vector<T>>
{
static constexpr specialised = true;
using OldType = T&;
}
template <class T, bool spec = TypeMatcher2<T>::specialised>
struct MyNeedfullTemplate
{
// generic version
};
template <class T>
struct MyNeedfullTemplate<T, true>
{
using OriginalT = typename TypeMatcher2<T>::OldType;
// specialised version
};