Reputation: 4275
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?
Upvotes: 5
Views: 3512
Reputation: 302663
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.
Upvotes: 3