5

The following code compiles and works on clang, but fails with "error: invalid use of non-static data member ‘Outer::a’" on gcc:

#include <functional>
#include <vector>
#include <assert.h>
#include <iostream>
#include <memory>

class Outer
{
public:
    bool a = false;

    virtual void f() = 0;

    template <typename T>
    class Inner : public T
    {
    public:
        virtual void f() override
        {
            a = true; // Note: accessed through inheritance, not through outer scope
        }
    };
};

struct Foo : Outer { };

int main()
{
    Outer::Inner<Foo> f;
    f.f();
}

Adding "this->a" to the inner class makes it work on both compilers, but I'm wondering what's the correct behavior and what the standards says about this.

Interestingly the above code works with as part of a larger code base in VS2017 at work, but when I try it at home with VS2017 in isolation, it fails with the same error as GCC.

You can try compiling it here:

SergeyA
  • 61,605
  • 5
  • 78
  • 137
monoceres
  • 4,722
  • 4
  • 38
  • 63
  • If you remove the code in `main()` and the `bool a` data member then the code fails in clang (which it shouldn't if it's being consistent). So I think it's a bug in clang. – David G Jan 09 '19 at 20:04
  • 3
    related: https://stackoverflow.com/questions/4643074/why-do-i-have-to-access-template-base-class-members-through-the-this-pointer – NathanOliver Jan 09 '19 at 20:06
  • 2
    maybe replace one of the tags with the `language-lawyer` tag – 463035818_is_not_an_ai Jan 09 '19 at 20:09
  • 2
    Should be bug on CLang. I can't understand how it can be not a compilation error. – SergeyA Jan 09 '19 at 20:09
  • 1
    @NathanOliver ah, not the first time I have caught myself on this. Seems like gcc is right then, the question then is why clang permits it. The example in your link doesn't. – monoceres Jan 09 '19 at 20:22
  • So I get what clang is doing, it is doing this: https://rextester.com/FRQ46060 -- it is looking up `a`, finding `Outer::a`. If you replace `a=7` with `Outer::a = 7;` then gcc also compiles it. I'm wondering (a) if `Outer::a = 7;` is also illegal, and if not what makes it different than `a = 7`. – Yakk - Adam Nevraumont Jan 09 '19 at 20:40
  • [here](http://coliru.stacked-crooked.com/a/dbfba5ff533fa000) is an interesting case; while `a=true` is a non-dependant expression, here I `if constexpr` prevent it from making the program ill-formed in any hypothetical instantiation. I think gcc gets the OP's problem right, but gets this one wrong. – Yakk - Adam Nevraumont Jan 09 '19 at 21:33

1 Answers1

4

This code is ill-formed no diagnostic required. So Gcc is right and friendly. And the absence of diagnostic for Clang and MSVC is just a compiler quality issue.

The rule of the standard involved is [temp.res]/8:

The validity of a template may be checked prior to any instantiation. [ Note: Knowing which names are type names allows the syntax of every template to be checked in this way. — end note  ] The program is ill-formed, no diagnostic required, if:

  • [..]

  • a hypothetical instantiation of a template immediately following its definition would be ill-formed due to a construct that does not depend on a template parameter, or [...]

In f body, the unqualified id-expression a does not depend on any template parameter, so this id-expression should be resolved at the point of definition of the template without the knowledge of any template argument. And at this point, this expression is ill-formed.


Note: a non qualified id-expression, (out of a class member access) expression is supposed to be a member only if it names a member of that class or of a non-dependent base [temp.dep.type]/5:

A name is a member of the current instantiation if it is:

  • An unqualified name that, when looked up, refers to at least one member of a class that is the current instantiation or a non-dependent base class thereof.
Oliv
  • 17,610
  • 1
  • 29
  • 72
  • Are you implying [this is also ill formed](https://rextester.com/FRQ46060), as `Outer::a` also doesn't depend on its template parameter? The validity of `Outer::a = 7` *does* depend on the tempate parameter, but the expression doesn't use `T`. So your answer being correct depends on what **exactly** the standard means by "construct that does not depend on a template parameter". – Yakk - Adam Nevraumont Jan 09 '19 at 20:38
  • @Yakk-AdamNevraumont The linked code I see is the exact same code as the one in the question. – Oliv Jan 09 '19 at 20:41
  • fixed the link, sorry – Yakk - Adam Nevraumont Jan 09 '19 at 20:43
  • @Yakk-AdamNevraumont I know the rule is that the implied object parameter is assumed only if the id-expression names a member of a the current class or a non dependent base. I can look for it in the standard. So `Outer::a` and `a` are equivalent according to this rule. – Oliv Jan 09 '19 at 20:49
  • @Yakk-AdamNevraumont I found the rule: [temp.dep.type/5](http://eel.is/c++draft/temp.dep#type-5.1). – Oliv Jan 09 '19 at 20:54
  • `Outer::a` is then [temp.dep.type/6](http://eel.is/c++draft/temp.dep#def:member_of_an_unknown_specialization) no? And then `a` should be treated the same? – Yakk - Adam Nevraumont Jan 09 '19 at 20:57
  • No, `a` is not a qualfiied id! – Yakk - Adam Nevraumont Jan 09 '19 at 21:01
  • @Yakk-AdamNevraumont `Outer` neither is a dependent type nor refers to the current instantiation. – Oliv Jan 09 '19 at 21:02
  • @Yakk-AdamNevraumont The rule (5.1): An *unqualified* name that, http://eel.is/c++draft/temp.dep#type-5.1 – Oliv Jan 09 '19 at 21:04