4

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?

Community
  • 1
  • 1
willj
  • 2,991
  • 12
  • 24
  • `A::f` is equivalent to `A::f` which could depend on the template parameter `T`. So why is this surprising? – Pradhan Dec 26 '14 at 23:43
  • To be dependent, a name must be a *member of an unknown specialization*. `A` is dependent, but also the *current instantiation* meaning that `A::f` is therefore not a *member of an unknown specialization*. – willj Dec 26 '14 at 23:50
  • There can be another overload of `A::f` in a different specialization of `A`. – David G Dec 26 '14 at 23:51
  • @0x499602D2 Yes, but as that requires a specialisation of `A`, it cannot cause any difference in meaning anywhere the `f(0)` gets used in the non-specialised `A`. –  Dec 27 '14 at 01:10
  • 1
    But beware: the rule isn't that errors involving non-dependent types must be diagnosed at template definition time, the rule is that a template that can never have a valid instantiation may be diagnosed at template definition time. Compiler treatment will vary. Some may suppress errors even for non-type-dependent expressions, some others may be able to diagnose some errors even for type-dependent expressions. (Not that I'm saying that is or isn't the case here.) –  Dec 27 '14 at 01:13
  • 1
    `typename` **prohibited**? That is actually impossible since C++11. – Columbo Dec 27 '14 at 11:37

1 Answers1

3

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.

Community
  • 1
  • 1
  • Yeah, that's the basic argumentation that I had in mind as well. Should we write bug reports? – Columbo Dec 27 '14 at 11:48
  • I'd rather do some more investigating first, at the very least making sure to check the most current versions of compilers, and if they still treat it as dependent, see if there's any documentation to explain why or explain that it's a known issue. I'd only report it as a bug after that. But if you feel differently, feel free to report directly. –  Dec 27 '14 at 11:50
  • We're dealing with the [most recent stable versions of GCC and Clang](http://coliru.stacked-crooked.com/a/51db653afdab25e3). I could test the Clang SVN trunk and so forth, but I reckon they give you equivalent results. We can also ask in the ISO C++ Google group. – Columbo Dec 27 '14 at 11:53
  • @Columbo I think the test I've now included in my answer shows at least one defininte bug in clang. It makes no sense whatsoever to have `b()` non-dependent, but `decltype(b())` dependent. Regardless of the other examples, that alone warrants a bug IMO. –  Dec 27 '14 at 12:40
  • Reported as http://llvm.org/bugs/show_bug.cgi?id=22038. Not tested with a recent enough version of GCC to report to them too yet. –  Dec 27 '14 at 13:03