Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Template parameters not deducible in partial specialization in gcc6, for a case that used to work in gcc5

This code results in an error in gcc6 (but works fine in gcc 4.8, 5.2 and clang 3.6):

template <typename T>
struct outer
{
    template <typename U>
    struct inner
    {

    };
};


template <typename T>
struct is_inner_for
{

    template <typename Whatever>
    struct predicate
    {
        static constexpr bool value = false;
    };

    template <typename U>
    struct predicate<typename outer<T>::template inner<U>>
    {
        static constexpr bool value = true;
    };
};

static_assert(
    is_inner_for<int>::template predicate<
        outer<int>::inner<double>
    >::value,
    "Yay!"
);

The error is:

main.cpp:22:9: error: template parameters not deducible in partial specialization:
  struct predicate<typename outer<T>::template inner<U>> : std::true_type
         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
main.cpp:22:9: note:         'U'
 ^~~~~~~~~~~~~

Commandline is:

g++ -std=c++1y -c main.cpp

See godbolt output here.

I have filed a bug report with gcc here: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=70141

However it was marked as invalid (I believe wrongly). The outer<T> that is used inside predicate is a concrete type at that point, so it is not a non-deduced context.

Is there anything in the standard that prevents this being valid c++ code?

like image 477
Alexander Kondratskiy Avatar asked Oct 18 '22 13:10

Alexander Kondratskiy


1 Answers

I suspect this is a bug in gcc 6.0, and an incorrect warning in clang 3.9 (the warning is weird - because the warning implies that the partial specialization would not be chosen, but if it were not chosen, the static assert would trigger).


From [temp.class.spec.match]:

A partial specialization matches a given actual template argument list if the template arguments of the partial specialization can be deduced from the actual template argument list

Can we deduce U in typename outer<T>::template inner<U> from outer<int>::inner<double>?

From [temp.deduct.type]:

If a template parameter is used only in non-deduced contexts and is not explicitly specified, template argument deduction fails.

The non-deduced contexts are:
— The nested-name-specifier of a type that was specified using a qualified-id.
— [...]

But the nested-name-specified here is typename outer<T>, which does not contain the type we're trying to deduce. None of the other non-deduced contexts apply. So deduction should succeed here.

Consider the following equivalent situation:

#include <utility>

template <class >
struct outer
{
    template <class U> struct inner {};
};

template <class T>
struct bar {
    template <class U> std::false_type foo(U const&);
    template <class U> std::true_type foo(typename outer<T>::template inner<U> const&);
};


int main() {
    static_assert(decltype(bar<int>{}.foo(outer<int>::inner<double>{}))::value, "!");
}

Both gcc 6.0 and clang 3.9 compile this code without warning - but this is the same sort of deduction that would happen in the partial specialization in the original example.

like image 144
Barry Avatar answered Nov 15 '22 13:11

Barry