0

I don't understand why the following:

template <typename...U>
struct always_false : std::false_type {};

template <size_t I, typename FRONT, typename...Ts>
struct split_impl
{
    static_assert(always_false<Ts...>::value, "Not enough types to index");
};

template <typename...Ts, typename...Us>
struct split_impl<0, std::tuple<Ts...>, Us...>
{
    using front = std::tuple<Ts...>;
    using rest = std::tuple<Us...>;
};

template <size_t I, typename...Ts, typename T, typename...Us>
struct split_impl<I, std::tuple<Ts...>, T, Us...>
    : split_impl<I - 1, std::tuple<Ts..., T>, Us...>
{};

template <size_t I, typename...Ts>
struct split : split_impl<I, std::tuple<>, Ts...> {};

template<int> struct x {};

split<0, x<0>, x<1>>::rest z = nullptr;

results in an ambiguous partial specialization:

source_file.cpp:15:8: note: partial specialization matches [with Ts = <>, Us = <x<0>, x<1>>]
struct split_impl<0, std::tuple<Ts...>, Us...>
       ^
source_file.cpp:22:8: note: partial specialization matches [with I = 0, Ts = <>, T = x<0>, Us = <x<1>>]
struct split_impl<I, std::tuple<Ts...>, T, Us...>
       ^

From my POV, when the first parameter is 0, it must accept the first as being a match.

Please note that I'm not looking for a way to get around this, I'm asking why this is not working. What partial specialization rules are forbidding this? This happens in clang, g++ and I assume vc++ is not matching for the same reason, even though the error is not helpful.

Adrian
  • 10,246
  • 4
  • 44
  • 110
  • 2
    As far as I can tell it's because of the extra `T` in the 2nd specialization. – bolov Dec 03 '17 at 16:08
  • oh, and btw a `static_assert` that is false for any possible imaginary instantiations of the main template (without considering specializations) makes "the program is ill-formed; no diagnostic is required." See https://stackoverflow.com/questions/30078818/static-assert-dependent-on-non-type-template-parameter-different-behavior-on-gc – bolov Dec 03 '17 at 16:11
  • @bolov, AFAIK, because it is a dependent value of a dependent type of a template parameter, this is well formed. – Adrian Dec 03 '17 at 16:17
  • see the linked post. There is a discussion about exactly this: if it is dependent on the template. It turns out it isn't dependent because it has the same value regardless of the template parameter provided. – bolov Dec 03 '17 at 16:19
  • @bolov, I think you should reread that Q&A again. – Adrian Dec 03 '17 at 16:23
  • I did. It is my question :p. There are 2 answers from high rep users backed up by the standard that tell what I just told you. – bolov Dec 03 '17 at 16:25
  • at first, I - just like you now - I challenged their conclusion, but in the end they showed they are right. – bolov Dec 03 '17 at 16:26
  • `Inside a template, some constructs have semantics which may differ from one instantiation to another. Such a construct depends on the template parameters.` "The construct `sizeof(answer) != sizeof(answer)` does not differ from one instantiation to another. So such a construct does not depend on the template parameters. Which means the entire `static_assert` does not depend on the template parameter." – Adrian Dec 03 '17 at 16:30
  • @bolov, it is because the template `always_false` *could* be specialized to cause a different result, that makes this valid. This is why what you did is invalid, and what I did is valid. If that were not the case, nothing would work with `static_assert`. – Adrian Dec 03 '17 at 16:31
  • @bolov, read this: https://stackoverflow.com/a/42913797/1366368. That Q is mine. ;) – Adrian Dec 03 '17 at 16:47
  • @bolov, actually, I did do something wrong. I used an alias, which can't be specialized, instead of a class which could. Fixed. Thanks. – Adrian Dec 03 '17 at 16:56
  • I see what you are saying about `always_false`. It is a bit out of my depth, but I think you could indeed be right. – bolov Dec 03 '17 at 20:52

1 Answers1

4

This is because of partial ordering. Both of your partial specializations are in another way a better match than the other, and so the call is ambiguous, because the compiler cannot say which one of the two is the better match if they are both equally good.

The first specialization is better than the second one because, as you said, 0 is used. An exact match is better than the general case from the second specialization.

The second specialization is better than the first one because it takes a single type parameter, instead of a variadic. Basically, T, Ts... is better than just Ts... for partial ordering.

Rakete1111
  • 47,013
  • 16
  • 123
  • 162