I came across an inconsistency in the way current C++ compilers (clang/gcc) determine whether a name is dependent. In the following example, A::f
is dependent but ::f
is not, resulting in an error when the latter is used.
template<typename>
struct B
{
typedef int Type;
};
template<typename U>
static U f(U u);
template<typename T>
struct A
{
template<typename U>
static U f(U u);
typename B<decltype(f(0))>::Type m1; // typename required
B<decltype(::f(0))>::Type m2; // typename not required
};
The inconsistent part is that the declaration of A::f
does not depend on a template parameter of A
, meaning it seems unnecessary to treat it as a dependent name.
This behaviour seems to be covered by the following wording in the C++11 standard:
[temp.dep.expr]/3
An id-expression is type-dependent if it contains
- an identifier associated by name lookup with one or more declarations declared with a dependent type
[temp.dep.type]/3
A type is dependent if it is
- a compound type constructed from any dependent type
The declaration of ::f
is clearly not dependent, as its type depends only on its own template parameters. Why should A::f
be treated differently?
I think that based on the standard, f
is non-dependent, actually.
14.6.2.2 Type-dependent expressions [temp.dep.expr]
3 An id-expression is type-dependent if it contains
- an identifier associated by name lookup with one or more declarations declared with a dependent type,
This applies equally to the global template function as it does to the member template function: not at all. The return type of U
is dependent inside the definitions of the template functions, but for the caller, the function type of f<int>
has already been transformed from U(U)
to int(int)
. At any rate, it wouldn't explain why compilers treat the two cases differently, and it also would not explain why a non-template member function is also treated as dependent.
- a template-id that is dependent,
- a conversion-function-id that specifies a dependent type, or
These do not apply. There is no <
or >
that must always be present in a template-id, and there is no conversion function being called.
- a nested-name-specifier or a qualified-id that names a member of an unknown specialization;
See below.
or if it names a static data member of the current instantiation that has type "array of unknown bound of
T
" for someT
(14.5.1.3).
This also does not apply: there are no arrays involved.
So it depends on whether f
is a member of an unknown specialization. But it isn't:
14.6.2.1 Dependent types [temp.dep.type]
5 A name is a member of an unknown specialization if it is
- A qualified-id in which [...].
- A qualified-id in which [...].
- An id-expression denoting the member in a class member access expression (5.2.5) in which [...].
These cannot apply: f
is neither qualified nor part of a class member access expression.
Since the only way f
can be dependent is if it is a member of an unknown specialization, and it is not a member of an unknown specialization, f
must not be dependent.
As for why compilers nonetheless treat it as dependent, I have no answer. Either some part of my answer here is wrong, the compilers have bugs, or the compilers follow a different version of the C++ standard. Testing with an example that works regardless of whether names are dependent shows a few variations in compiler treatment:
#include <cstdio>
void f(const char *s, ...) { std::printf("%s: non-dependent\n", s); }
struct S1 { };
template <typename T>
struct S2 {
static S1 a;
static S1 b() { return {}; }
template <typename U>
static U c() { return {}; }
static void z() {
f("S1()", S1()); // sanity check: clearly non-dependent
f("T()", T()); // sanity check: clearly dependent
f("a", a); // compiler agreement: non-dependent
f("b()", b()); // compiler disagreement: dependent according to GCC 4.8, non-dependent according to clang
f("c<T>()", c<T>()); // sanity check: clearly dependent
f("c<S1>()", c<S1>()); // compiler agreement: dependent
f("decltype(b())()", decltype(b())()); // compiler agreement: dependent
}
};
void f(const char *s, S1) { std::printf("%s: dependent\n", s); }
// Just to show it's possible to specialize the members
// without specializing the full template.
template <>
S1 S2<S1>::b() { return {}; }
template <>
template <>
S1 S2<S1>::c<S1>() { return {}; }
int main() {
S2<S1>::z();
}
This difference in clang's treatment of b()
, decltype(b())()
, and c<S1>()
is particularly troubling to me. It just doesn't make any sense. They're clearly all equally dependent. I can understand from an implementation point-of-view that care must be taken not to generate code for the member functions just yet because there might be specialisations of S2<S1>::b
or S2<S1>::c<S1>
, but that applies to all, and has no effect on the return type.