C ++ 17中Clang的歧义局部专业化

时间:2018-07-01 20:52:51

标签: c++ c++17 template-specialization

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(尽管使用{{3 }}一切都很好)。

消息错误是:

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专业化。

1 个答案:

答案 0 :(得分:5)

此处的C ++ 17区别在于,您可以从相应的参数推导出非类型参数的类型。 Clang显然在做推论错误。

与此处相关,您应该为Foo综合一个唯一类型,并尝试针对Foo推导PartId中的THelper<Foo, TSelect<Foo, PartId>>THelper<Unique, TSelect<Unique, AnotherOneSelector<Unique>::Id>>。似乎正在发生的事情是Clang认为AnotherOneSelector<Unique>::Id具有一些单独的唯一类型-称为Unique2-,因此在C ++ 17中推论失败,因为您推导了Foo的冲突类型。众所周知,对这样的非推导上下文的处理未得到充分指定,但是我敢肯定,这是要使用转换后的模板参数的类型而不是原始参数来推导。

两个可能的解决方法是:

  • 通过将类型包装到非推断的上下文中来抑制非类型自变量中的Foo的推断。例如:template <typename Foo, std::remove_const_t<Foo> PartId>
  • 在模板参数中强制转换为Foo以避免虚假冲突:struct THelper<Foo, TSelect<Foo, Foo{AnotherOneSelector<Foo>::Id}>>