Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++11/14/17, GCC 7 vs GCC 8: Name lookup for friend class templates

I'm trying to figure out while the following code works in GCC 7, but not in GCC 8.1.

What the code does is:

  • define (and forward declare) a class template MyGoodFriend (in global namespace)
  • define a class template Befriended inside the inner namespace
  • Make all specializations of MyGoodFriend a friend of Befriended

The problematic piece is

        template<class FA>
        friend class MyGoodFriend;

I get what the problem is. GCC 8.1 requires me to use the fully-qualified name ::MyGoodFriend in the friend declaration - however, GCC 7 was happy with just MyGoodFriend. This is the code:

template<class A>
class MyGoodFriend;

namespace inner {
    template<class T>
    class Befriended {
    private:
        int i;
        T t;

        template<class FA>
        friend class MyGoodFriend;
        // This works for gcc 8.1:
        // template<class FA>
        //friend class ::MyGoodFriend;
    };
} // namespace inner

template<class A>
class MyGoodFriend {
public:
    void do_something() {
        inner::Befriended<bool> bf;
        bf.i = 42;
    }
};

int main() {
    MyGoodFriend<int> mgf;
    mgf.do_something();
}

You can test this with GCC 7 vs 8 here: https://godbolt.org/g/6u9rgy

Two questions:

Why did the behavior of GCC change?

Was GCC 7 misinterpreting the standard? Or is this a mistake in GCC 8?

If GCC 8 is correct: Why?

If I read the standard correctly (referring to the C++14 standard here): Section 3.4 (which specifies how name lookup works), Point 7.4 states:

A name used in the definition of a class X […]

  • if X is a member of namespace N, or is a nested class of a class that is a member of N, or is a local class or a nested class within a local class of a function that is a member of N, before the definition of class X in namespace N or in one of N ’s enclosing namespaces

Obviously, MyGoodFriend is declared in an enclosing namespace, so it should be visible in Befriended - right?

Thanks for any help!

like image 817
Lukas Barth Avatar asked Jun 13 '18 15:06

Lukas Barth


1 Answers

From [namespace.memdef]/3, emphasis mine (the wording is the same in C++11):

If a friend declaration in a non-local class first declares a class, function, class template or function template99 the friend is a member of the innermost enclosing namespace. [...] If the name in a friend declaration is neither qualified nor a template-id and the declaration is a function or an elaborated-type-specifier, the lookup to determine whether the entity has been previously declared shall not consider any scopes outside the innermost enclosing namespace.

That is, when you write:

template<class FA>
friend class MyGoodFriend;

We only look for inner::MyGoodFriend, not ::MyGoodFriend. Since we don't find it, we consider it to be a forward declaration of the class template inner::MyGoodFriend. As a result, ::MyGoodFriend is not friended.


With the revision, gcc 7 compiled likely because of one of the many template-access related bugs. See this meta-bug. gcc 8's behavior is correct.

like image 157
Barry Avatar answered Nov 06 '22 18:11

Barry