5

Current C++ compilers (latest gcc, clang) require the typename keyword in the example below:

template<class T>
struct A
{
};

template<class T>
void f(T)
{
    struct C
    {
    };
    typedef typename A<C>::Type Type; // typename required
}

If typename is omitted gcc (4.9, 5.0) reports the error:

need 'typename' before 'A<f(T)::C>::Type' because 'A<f(T)::C>' is a dependent scope

This example is otherwise well-formed according to my reading of the C++11 standard.

This behaviour seems to be covered by the following wording:

[temp.dep.type]/8

A type is dependent if it is

  • a template parameter,

  • a member of an unknown specialization,

  • a nested class or enumeration that is a member of the current instantiation,

  • a cv-qualified type where the cv-unqualified type is dependent,

  • a compound type constructed from any dependent type,

  • an array type constructed from any dependent type or whose size is specified by a constant expression that is value-dependent,

  • a simple-template-id in which either the template name is a template parameter or any of the template arguments is a dependent type or an expression that is type-dependent or value-dependent, or

  • denoted by decltype(expression), where expression is type-dependent.

However, according to [class.local] the class C is a local class rather than a nested class. If so, why should A<C> be treated as dependent?

EDIT

For bonus points, if the example is modified by adding a member enum to C as follows:

template<typename T>
struct A
{
    typedef T Type;
};

template<class T>
void f(T)
{
    struct C
    {
        enum { value = T::value };
    };
    typedef typename A<C>::Type Type; // typename required
}

Should A<C> now be treated as dependent?

willj
  • 2,991
  • 12
  • 24
  • 2
    This seems to be related to DR 1484: http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html – willj Dec 29 '14 at 12:26
  • 2
    They use anchors on the DR page, so you can provide a link directly to the specific defect: http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#1484 – dyp Dec 29 '14 at 12:27
  • 1
    I think the `typename` is required. Consider weird examples like: http://coliru.stacked-crooked.com/a/a03aebd672bfa7c7 We can make *the definition* of `C` dependent on the template parameter, even if its name is not. – dyp Dec 29 '14 at 12:37
  • [temp.dep.type]/8 -> http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3337.pdf – willj Dec 29 '14 at 12:46
  • 1
    Ah, thanks. So you left out some of the bullet points. None of them seem to apply, though, as far as I can see. That seems like an oversight in the standard to me. – dyp Dec 29 '14 at 12:50
  • @dyp Agreed. If the definition of `C` is dependent then it follows that `A` has to be dependent. Although this example the definition of `C` is not dependent.. – willj Dec 29 '14 at 12:59
  • The error message does not say `C` is a dependent type but its scope `f` is a dependent name. – Lingxi Dec 29 '14 at 13:00
  • @Lingxi Indeed. The C++ standard does not mention the term 'dependent scope' - this seems to be something invented by gcc. – willj Dec 29 '14 at 13:01

3 Answers3

4

According to my understanding (and the current wording of the standard), C in your example is not dependent. Neither is A<C>::Type, so the typename is not required.

There is a fundamental difference between nested classes of class templates and local classes in function templates: The latter cannot be specialized, thus any reference to a local class inside a function template is uniform. That is, in every specialization of f, C refers to the class C that is defined in this function template f. That is not the case with class templates as you can indeed explicitly specialize members on their own (as covered in [temp.expl.spec] /(1.6)):

template <typename T>
class A { class C{}; };

template <>
class A<int>::C { int i; };

However:

A type is dependent if it is

  • a compound type constructed from any dependent type,

So if the definition was done as in dyp's example, C would be dependent as it is constructed from T.
There are unclarities in the standards wording that are being discussed in the comment section, e.g. about definitions of member functions that depend on T and how that transposes to the classes dependency.

Columbo
  • 60,038
  • 8
  • 155
  • 203
  • To be clear, *all* nested classes defined in a template class are dependent. In `template struct A { struct B { }; void f() { B b; } };`, the change in wording that you found does not make `B` non-dependent. It cannot, because there could be a specialisation for e.g. `A::B`. But I do think I'm now in agreement with you that local classes can be non-dependent. –  Dec 29 '14 at 13:21
  • You can specialise `A::B` even without specialising `A`: `template struct A { struct B { int && m; }; void f() { B b; } }; template <> struct A::B { }; int main() { A().f(); }` is perfectly valid. –  Dec 29 '14 at 13:29
  • @hvd Ahh, you are correct. *"A name is a dependent member of the current instantiation if it is a member of the current instantiation that, when looked up, refers to at least one member of a class that is the current instantiation."* I'll edit my answer shortly. – Columbo Dec 29 '14 at 13:30
  • Can we make the determination of whether the local class is dependent or not arbitrarily complex? E.g. `struct C { auto foo() { return std::conditional< std::is_trivial::value, int, bool >::type{}; } };` – dyp Dec 29 '14 at 13:37
  • @dyp On the first glance, `C` is not dependent, but `C::foo` is. – Columbo Dec 29 '14 at 13:42
  • @Columbo Well we could construct a variation of my earlier example, where the "kind" of the expression `A::Type` depends on `C::foo`, no? – dyp Dec 29 '14 at 13:43
  • But if `C::foo` is dependent, `C` must necessarily be dependent too. `C` might be used as a template argument for a template class that uses SFINAE to detect whether it has any `int foo()` member. If the standard doesn't say that, that seems like an oversight to me. –  Dec 29 '14 at 13:43
  • @hvd The part "constructed from any dependent type" could include return types of member functions; but I'm not sure if it's a good idea to require "deep inspection" of local classes to determine whether or not a dependent type is *used* somewhere. – dyp Dec 29 '14 at 13:46
  • @hvd I made my answer a community wiki, feel free to improve it. – Columbo Dec 29 '14 at 13:49
  • @dyp Halfway agreed. Considering something like `struct C { size_t foo() { return sizeof(T); } };`, I think a fair argument can be made that based on the current standard, `C` is not dependent: the type of its `foo` member is `size_t()`, which is not a dependent type. But the signature of your `foo` member is `typename std::conditional< std::is_trivial::value, int, bool >::type()`, which is a dependent type. –  Dec 29 '14 at 13:49
  • @Columbo I don't think there's much to add to what you already put in your answer, actually. We're having some concerns about the impact of the current wording in the standard, but as far as I can tell, not about what the standard actually says. –  Dec 29 '14 at 13:52
  • I'm not quite sure I understand what you're saying wrt to your `C`. Do you mean it's *not* dependent? If so, what about `struct C { constexpr static size_t foo() { return sizeof(T); } };`? Or are you saying it *is* not dependent, but it *should* be? – dyp Dec 29 '14 at 13:52
  • @dyp Yeah, that was what I was saying, and yeah, that's a good example showing that what I was saying can't be right. I think you've just given a good example that the current wording of the standard, or at least my interpretation of it, puts impossible requirements on implementations, so at least something's wrong, but that something might just be me. –  Dec 29 '14 at 13:56
  • @hvd I think we can agree that the standard is imprecise about the dependency of local classes in function templates. However, I'm not sure whether there is any point in writing an EWG-issue or DR. – Columbo Dec 29 '14 at 14:06
  • I've appended a modified example to the original question which demonstrates a case where `C` *must* be treated as dependent, while this answer's interpretation of the standard does not require it. – willj Dec 29 '14 at 17:03
  • @willj `C` is clearly dependent (for two reasons), hence `A` is dependent. – Columbo Dec 29 '14 at 17:04
  • @Columbo Sorry, there was a mistake in the modified example! – willj Dec 29 '14 at 17:29
  • @willj `C` is still dependent as it is constructed from a dependent type, `T`. – Columbo Dec 29 '14 at 17:31
  • @Columbo I suppose you could interpret "constructed from any dependent type" as "contains a member with a value-dependent initializer" .. but that seems too broad, not to mention difficult to implement. – willj Dec 29 '14 at 17:38
  • @willj But what else would "construct" mean? If you think that the standard is defected or vague in this respect, write a DR. I'm just using my interpretation. :) – Columbo Dec 29 '14 at 17:41
0

Following is my reasoning, hope it helps. The local C does not instantiate until f instantiate. So, A<C> is not an instantiation and is opaque to the compiler when it sees it. Because of the opaqueness, the compiler cannot determine whether A<C>::Type is a nested type name or a data member or a method. However, by default, the compiler does not see A<C>::Type as a nested type name. Therefore, an explicit specification is needed.

Lingxi
  • 14,579
  • 2
  • 37
  • 93
  • This seems correct, however the question is tagged with language-lawyer and refers to the standard, so you need to back up your claims with official rules of the language. – BartoszKP Dec 29 '14 at 12:37
  • @BartoszKP You are right. Anyway, here is some conjecture for your interest. The error message produced by the compiler seems to suggest that, a nested type, even if nondependent itself, would require an explicit specification if its scope is a dependent type. I guess it is implementation-defined whether the nested type should instantiate in this case regardless of its scope. – Lingxi Dec 29 '14 at 12:53
  • I don't want to discuss this matter, I was only trying to encourage you to improve your answer :) As you can see in Columbo's answer, the claim that it's "implementation-defined" is wrong anyway. – BartoszKP Dec 29 '14 at 13:14
0

There doesn't appear to be anything in the standard to state that the typename keyword should be necessary here. The wording does not explicitly state otherwise, either, which may have led GCC to take a bit of a shortcut in treating f<T>(T)::C (being a local class in a function template specialisation) as dependent on T — by extension, this would make A<[f<T>(T)::]C>::Type dependent.

Core defect 1484 wasn't raised for this issue specifically, but I think the additional non-normative text it proposes makes the intention clear and, were it in a standard, GCC would compliantly not require the typename keyword here.

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055