多个类模板特化是否有效,当每个模板特化仅在非推导上下文中涉及模板参数的模式之间不同时?
std::void_t
的一个常见示例用它来定义一个特征,该特征揭示一个类型是否有成员typedef
被称为"类型"。这里采用单一专业化。这可以扩展为识别一个类型是否 成员typedef
调用" type1"或者一个名为" 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 {};
答案 0 :(得分:7)
规则部分特化必须比主模板更专业 - 您的两个专业都遵循该规则。但是没有一条规则规定部分专业化永远不会含糊不清。更重要的是 - 如果实例化导致模糊的专业化,那么该程序就会形成错误。但这种模糊的实例化必须首先发生!
看来clang在这里遇到了CWG 1558,并且过于渴望在。 product_id int null
中替换void
这几乎是CWG 1980:
在像
这样的例子中std::void_t
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();
的第二个声明似乎是对第一个声明的重新声明,但SFINAE可以区分,即等效但不具有功能等同。
如果您使用f
的非别名实现:
void_t
然后clang允许两种不同的专业化。当然,在同时具有template <class... Ts> struct make_void { using type = void; };
template <class... Ts> using void_t = typename make_void<Ts...>::type;
和has_members
typedef错误的类型上实例化type1
,但这是预期的。
答案 1 :(得分:1)
我不相信它是正确的,或者至少,如果我们实例化具有同时嵌套了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已经写好。