Deducing first template argument with other template parameters defaulted

元气小坏坏 提交于 2019-12-17 09:43:37

问题


Gcc and clang seem to disagree on whether this code should compile or not:

#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
>);

See godbolt output here and try it. I'm siding with clang here, but what does the C++ standard say about this?

A follow-up question - can this be made to work in clang?


回答1:


This is perfectly valid code, and gcc is right. The "feature" was introduced in C++17. It's not really a feature because it is a defect report. MyDelegate matches the partial specialization of signature_traits, and so it should be taken as gcc correctly does. Note that it works because the second template parameter is defaulted.

The reason why clang doesn't compile it is because that defect report has a defect :P. It doesn't introduce the appropriate change in partial ordering, which is not really nice and makes previousy valid code ambiguous again.

It is expected to be fixed soon, but in the meanwhile, clang decided to "hide" the feature behind a flag, -frelaxed-template-template-args.

So, just compile with that flag enabled and you should be fine.




回答2:


The problem is that MyDelegate doesn't match template <typename> class because receive two template parameter: a type (Signature) and an int (N).

Yes: the second one has a default value. But the signature remain template <typename, int> class.

So I suppose g++ is wrong (compiling without error) and clang++ is right. Rakete1111 corrected me (thanks!): your code was wrong before C++17 but correct starting from C++17 (see his answer for the references). So (you're compiling C++17) g++ is right and clang++ is wrong.

A possible solution (waiting for a correct clang++) is define signature_traits as follows

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

or, better IMHO, adding the integer parameter

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

Observe that both solutions are compatible with

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


来源:https://stackoverflow.com/questions/48446983/deducing-first-template-argument-with-other-template-parameters-defaulted

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