Search code examples
c++templateslanguage-lawyerdependent-name

Is a function type dependent if it depends only on its own template parameters?


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?


Solution

  • 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 some T (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.