C ++ 17中Clang的歧义局部专业化 [英] Ambiguous partial specializations with Clang in C++17
问题描述
template <typename Foo, Foo Part>
struct TSelect {};
enum What {
The
};
template <typename Foo>
struct AnotherOneSelector {
static constexpr Foo Id = Foo::The;
};
template <typename Foo, typename SelectPartType>
struct THelper;
template <typename Foo>
struct THelper<Foo, TSelect<Foo, AnotherOneSelector<Foo>::Id>> {};
template <typename Foo, Foo PartId>
struct THelper<Foo, TSelect<Foo, PartId>> {};
int main() {
THelper<What, TSelect<What, What::The>> t;
}
此代码使用gcc8.1编译,并带有每个标准选项(c + + 11,c ++ 14,c ++ 17),但clang干线不带有 c ++ 17 (尽管使用 c ++ 14 一切都很好)。
This code compiles with gcc8.1 with each of the standard option (c++11, c++14, c++17), but clang trunk does not with c++17 (although with c++14 everything is fine).
消息错误是:
test.cpp:23:49: error: ambiguous partial specializations of 'THelper<What, TSelect<What, The> >'
THelper<What, TSelect<What, What::The>> t;
^
test.cpp:17:12: note: partial specialization matches [with Foo = What]
struct THelper<Foo, TSelect<Foo, AnotherOneSelector<Foo>::Id>> {};
^
test.cpp:20:12: note: partial specialization matches [with Foo = What, PartId = The]
struct THelper<Foo, TSelect<Foo, PartId>> {};
^
1 error generated.
哪个编译器正确?我没有在C ++ 17中看到模板
专业化方面的任何更改。
Which compiler is correct? I haven't seen any changes in template specialization in C++17.
推荐答案
C ++ 17此处的区别在于,您可以从相应的参数推导出非类型参数的类型。而且Clang显然在做推论错误。
The C++17 difference here is that you can deduce the type of a non-type parameter from the corresponding argument. And Clang is apparently doing the deduction wrong.
与此相关,您应该为 Foo
合成一个唯一的类型,并尝试推导<$ c在 THelper< Foo,TSelect< Foo,PartId>>> $ c $中的$ c> Foo
和 PartId
c>反对 THelper< Unique,TSelect< Unique,AnotherOneSelector< Unique> :: Id>>
。似乎正在发生的事情是Clang将 AnotherOneSelector< Unique> :: Id
视为具有一些单独的唯一类型-称为 Unique2
-,使得推论在C ++ 17中失败,因为您推导了 Foo
的类型冲突。众所周知,对这样的非推导上下文的处理未得到充分指定,但是我敢肯定,这是要使用转换后的模板参数的类型而不是原始类型来推论。
As relevant here, you are supposed to synthesize a unique type for Foo
and try to deduce the Foo
and PartId
in THelper<Foo, TSelect<Foo, PartId>>
against THelper<Unique, TSelect<Unique, AnotherOneSelector<Unique>::Id>>
. What seems to be happening is that Clang treats AnotherOneSelector<Unique>::Id
to have some separate unique type - call it Unique2
- so that the deduction fails in C++17 because you deduced conflicting types for Foo
. The handling of non-deduced contexts like this is notoriously underspecified, but I'm pretty sure it's meant to deduce using the converted template argument's type rather than the original.
两个可能的解决方法是:
Two possible workarounds are:
- 禁止从非类型参数中减去
Foo
将类型包装到非推断上下文中。例如:template< typename Foo,std :: remove_const_t< Foo> PartId>
。 - 在模板参数中强制转换为
Foo
以避免虚假冲突:struct THelper< Foo,TSelect< Foo,Foo {AnotherOneSelector< Foo> :: Id}>>>
- Suppress deduction of
Foo
from the non-type argument by wrapping the type into a non-deduced context. For example:template <typename Foo, std::remove_const_t<Foo> PartId>
. - Force the conversion to
Foo
in the template argument to avoid the spurious conflict:struct THelper<Foo, TSelect<Foo, Foo{AnotherOneSelector<Foo>::Id}>>
这篇关于C ++ 17中Clang的歧义局部专业化的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!