Ambiguous partial specializations with Clang in C++17

落爺英雄遲暮 提交于 2019-12-21 07:36:25

问题


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

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).

Message error is:

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.

Which compiler is correct? I haven't seen any changes in template specialization in C++17.


回答1:


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.

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:

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


来源:https://stackoverflow.com/questions/51126940/ambiguous-partial-specializations-with-clang-in-c17

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!