使用默认的其他模板参数推导出第一个模板参数

时间:2018-01-25 15:54:30

标签: c++ templates language-lawyer template-meta-programming c++17

Gcc和clang似乎不同意这段代码是否应该编译:

#include <type_traits>

template <typename Signature, int N = 0>
struct MyDelegate { };

template <typename D>
struct signature_traits;

template <template <typename> class Delegate, typename Signature>
struct signature_traits<Delegate<Signature>>
{
    using type = Signature;
};

static_assert(std::is_same_v<
    void(int, int),
    signature_traits<MyDelegate<void(int, int)>>::type
>);

godbolt output here and try it。我在这里支持clang,但是C ++标准对此有何看法?

后续问题 - 这可以在clang中使用吗?

2 个答案:

答案 0 :(得分:13)

这是完全有效的代码,gcc是对的。 &#34;功能&#34;是introduced in C++17。它不是一个真正的功能,因为它是一个缺陷报告。 MyDelegate匹配signature_traits的部分特化,因此应该正确地将其视为gcc。请注意,它的工作原理是因为第二个模板参数是默认的。

clang没有编译它的原因是因为缺陷报告有缺陷:P。它doesn't introduce the appropriate change in partial ordering,这不是很好,并使以前的有效代码再次模糊不清。

预计很快就会被修复,但与此同时,clang决定隐藏&#34;标志背后的功能,-frelaxed-template-template-args。

所以,只需启用该标志进行编译,你应该没问题。

答案 1 :(得分:0)

问题是MyDelegatetemplate <typename> class不匹配,因为接收两个模板参数:类型(Signature intN)。

是:第二个具有默认值。但签名仍为template <typename, int> class

所以我认为g ++是错误的(编译没有错误)和clang ++是对的。 Rakete1111纠正了我(谢谢!):你的代码错误之前 C ++ 17但从C ++ 17开始是正确的(参见他对参考文献的回答)。所以(你正在编译C ++ 17)g ++是对的,而clang ++是错误的。

可能的解决方案(等待正确的clang ++)定义signature_traits如下

template <template <typename, int=0> class Delegate, typename Signature>
struct signature_traits<Delegate<Signature>>
{
    using type = Signature;
};

或者,更好的恕我直言,添加整数参数

template <template <typename, int> class Delegate, typename Signature, int N>
struct signature_traits<Delegate<Signature, N>>
{
    using type = Signature;
};

观察两种解决方案都与

兼容
static_assert(std::is_same<
    void(int, int),
    typename signature_traits<MyDelegate<void(int, int)>>::type
>::value);