问题
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