The port of some C++11 code from Clang to g++
template
using value_t = typename T::value_type;
template
struct S
{
using val
The problem relies on SFINAE. If you rewrite your member function to be value_t
, like the outside declaration, then GCC will happily compile it:>
template
struct S
{
using value_type = int;
static const value_t> C = 0;
};
template
const value_t> S::C;
Because the expression is now functionally equivalent. Things like substitution failure come into play on alias-templates, but as you see, the member function value_type const C
doesn't have the same "prototype" as value_t
. First one doesn't have to perform SFINAE, whereas the second one requires it. So clearly both declarations have different functionality, hence GCC's tantrum.> const S
Interestingly, Clang compiles it without a sign of abnormality. I assume it just so happens that the order of Clang's analyses are reversed, compared to GCC's. Once the alias-template expression is resolved and fine (i.e. it is well-formed), clang then compares both declarations and check it they are equivalent (which in this case they are, given both expressions resolve to value_type
).
Now, which one is correct from the standard's eyes? It's still an unresolved issue to whether consider alias-template's SFNIAE as part of its declaration's functionality. Quoting [temp.alias]/2:
When a template-id refers to the specialization of an alias template, it is equivalent to the associated type obtained by substitution of its template-arguments for the template-parameters in the type-id of the alias template.
In other words, these two are equivalent:
template
struct Alloc { /* ... */ };
template
using Vec = vector>;
Vec v;
vector> u;
Vec
and vector
are equivalent types, because after substitution is performed, both types end up being vector
. Note how "after substitution" means that the equivalence is only checked once all template arguments are replaced with the template parameters. That is, comparison starts when T
in vector
is replaced with int
from Vec
. Maybe that's what Clang is doing with value_t
? But then there's the following quote from [temp.alias]/3:>
However, if the template-id is dependent, subsequent template argument substitution still applies to the template-id. [Example:
template
using void_t = void; template void_t f(); f (); // error, int does not have a nested type foo — end example]
Here's the problem: the expression has to be well-formed, so the compiler needs to check whether the substitution is fine. When there is a dependence in order to perform template argument substitution (e.g. typename T::foo
), the functionality of the whole expression changes, and the definition of "equivalence" differs. For example, the following code won't compile (GCC and Clang):
struct X
{
template
auto foo(T) -> std::enable_if_t;
};
template
auto X::foo(T) -> void
{}
Because the outer foo
's prototype is functionally different from the inner one. Doing auto X::foo(T) -> std::enable_if_t
instead makes the code compile fine. It's so because the return type of foo
is an expression that is dependent on the result of sizeof(T) == 4
, so after template substitution, its prototype might be different from each instance of it. Whereas, auto X::foo(T) -> void
's return type is never different, which conflicts with the declaration inside X
. This is the very same issue that's happening with your code. So GCC seems to be correct in this case.