3

The following piece of code fails to compile properly with both G++ and Clang++, whatever the C++ standard required (98, 11, 14):

$ cat foo.cc
template <typename T>
struct foo
{
  friend void bar(){}
};

int main()
{
  foo<int> fi;
  foo<char> fc;
}

for instance:

$ clang++-mp-3.7 -std=c++14 foo.cc
foo.cc:4:15: error: redefinition of 'bar'
  friend void bar(){}
              ^
foo.cc:10:13: note: in instantiation of template class 'foo<char>' requested here
  foo<char> fc;
            ^
foo.cc:4:15: note: previous definition is here
  friend void bar(){}
              ^
1 error generated.

I know I can avoid this problem by putting the definition (not declaration) of bar out of foo, I'm not asking for a workaround. Rather, I would like to understand what's going on. So I have two questions:

  • First, I don't see where in the standard this behavior is documented, or implied. I doubt than both G++ and Clang++ would get this wrong, so I expected that it is documented/implied somewhere.

  • Second, what's the point of this behavior? There is value in defining friends inside the class (see for instance Is there any difference if we define friend function inside or outside of class), so what is the value of keeping this behavior. I mean, shouldn't the standard accept the behavior as one (I) would expect?

Community
  • 1
  • 1
akim
  • 8,255
  • 3
  • 44
  • 60
  • Interesting. My naive assumption would be that there should be two `bar`, one `foo::bar` and one `foo::bar` (well, not really, but close) -- each a friend in the lexical scope of the class in question. Only functions not defined inline should be in the scope of the enclosing namespace. But I don't understand that part of the standard well enough to make a good argument. – Yakk - Adam Nevraumont Oct 06 '15 at 12:59
  • 1
    It must be extremely unusual to have a friend of `foo` that doesn't depend on `T`. What can it do that needs friendship? – Bo Persson Oct 06 '15 at 13:42
  • This is because I stripped down the real case too eagerly. But actually, you helped me see what was really wrong in the real implementation: `bar` was again templated (by a `T2`) instead of just being how I wrote it here. But then, since its genuine signature does take a `foo` as argument, the generated signatures for the generated friends _are_ different, and everything works as expected. Thanks! – akim Oct 07 '15 at 07:11

1 Answers1

3

For each T a new class is created. So foo<int> is one class and foo<char> is another.

If you take your example and unwind it, it will look like this:

struct foo_int
{
    friend void bar() {}
};

struct foo_char
{
    friend void bar() {}
};


int main()
{
    foo_int fi;
    foo_char fc;
}

Through the friend keyword you are defining a static global (namespace scope) function named bar. And as you see here you are doing it twice.

Simon Kraemer
  • 5,700
  • 1
  • 19
  • 49
  • Yes, I can see that. But really I'm talking about friend functions, whose inner definitions are rather syntactic sugar for an external definition. What I'm looking for is the section of the standard that implies this, and second, and explanation of why this behavior is desirable. And finally, if this is really what the standard means, should compiler warn about such a case? – akim Oct 06 '15 at 13:18
  • @akim See [here](http://stackoverflow.com/questions/4114126/scope-of-friend-function-in-gcc) for the scope of friend functions that are declared in classes. And I don't see what the compiler should warn you about? You are getting an error which is totally fine. – Simon Kraemer Oct 06 '15 at 13:48
  • No, I did not get an error, someone else did. This was taken from a templated library, and an user instantiated twice `foo` with two different parameters in different place, and it resulted into an error on her side. I wish the compile would have warned me before. – akim Oct 06 '15 at 14:00
  • 1
    Erhm what? Template classes are created at compile time when they are used the first time. How should a compiler know before that this won't be ok. If you only use one specialication per compilation unit there is no problem with the code. – Simon Kraemer Oct 06 '15 at 14:07
  • You mean "classes are created at compile time", not "template classes". Yes, I agree. So I would like the compiler to warn when it instantiates a class template that contains the definition of friend functions, as it's likely to explode if there is another instantiation elsewhere. Sure, if there's a single instantiation in the compilation unit, there's no problem; but that's asking for trouble anyway! So yes, I would prefer a warning. – akim Oct 06 '15 at 14:44