3

I am learning friend declarations in C++ using the books listed here. So after reading, to test my understanding of the concept, i wrote the following program whose output i am unable to understand:

template<typename T>
struct Name
{
  
  friend void anotherFeed(int x)//anotherFeed is implicitly inline and its definition is generate only when we use this nonmember function so why are we getting error at instiantiation?
  {
  }
 
};
int main()
{
    Name<int> s;
    Name<double> p;//error here. My question is that this is instiantiation of the class template Name<double> and not a call to anotherFeed so why do we get error here?
}

The above program give the following error:

error: redefinition of ‘void anotherFeed(int)’

This is my current understanding:

  1. The friend non-member function anotherFeed(int) is implicitly inline.
  2. Even if anotherFeed(int) is inline, we cannot define the same function(doesn't matter inline or not) in the same translation unit.
  3. The definition of anotherFeed(int) is generated only when we use/call this function just like for a nontemplate member function of a class template.

My question is that: Assuming that my understanding(the above 3 points) are correct, since i have not called/used anotherFeed so its definition should not be generated and we should not get the redefinition error at the time of creating an instance of the class template. Only when we call anotherFeed using those instances, we should get the redefinition error. So why do we get error at the time of creating class template's instance. Is there anything wrong in any of the above 3 points.

Summary

I read that the definitions for these functions(non template member functions and friend non template functions) are instantiated only when used. That is,

Name<int> s;    
Name<double> p; //this should work in principle because this does not instantiate the definition of anotherFeed(int)

But this doesn't happen. Why/How?

Jason
  • 36,170
  • 5
  • 26
  • 60
  • Regarding the comment, "its definition is generated only when we use this nonmember function" isn't accurate. If the function itself were a template being instantiated, that would be true. But it isn't, so.. it isn't. It's embodied the moment you explicitly craft some `Name` – WhozCraig Feb 05 '22 at 13:10
  • @WhozCraig So does the same happen for nontemplate member functions of a class template? I mean say we have a class template `Name` and say that class template has a nontemplate member function. Then when we create an instance of that class template like say `Name p;`, will the definition for the nontemplate member function be generated just like for the friend function? – Jason Feb 05 '22 at 13:17
  • By defining the function within the definition of a template class, you force the compiler to define it for both instantiations of the template. i.e. there is a definition for class `Name` and another for `Name`. The function itself is not dependent on the template parameter (`T`) so every instantiation of your class template creates a new definition of the same function. – Peter Feb 05 '22 at 13:20
  • Yes. that's what happens. – WhozCraig Feb 05 '22 at 13:22
  • @Peter But i read in a book that the definitions are instantiated only when used. So take for example: `template struct Name { void feed(int x)//note no friend here { } }; int main() { Name p; }` So when i wrote `Name p` above, will `feed`'s definition be instantiated? – Jason Feb 05 '22 at 13:46
  • @Peter From documentation i have the following example: `template struct C { void f() { T x; } }; C c; // OK, definition of C​::​f is not instantiated at this point` . So as mentioned in the documentation, the definition of the non template member function `f` is not instantiated when writing `C c;`. I know there is `T x;` inside `f` which is a dependent name. But even if we replace `T x;` with say `float x;` then also the same thing applies? Doesn't it. That is, definition is instantiated only when used. – Jason Feb 05 '22 at 14:09
  • Note that `s.anotherFeed(5);` is illegal in any case. `anotherFeed` is not a member function. – HolyBlackCat Feb 05 '22 at 15:42
  • @Anya If you are looking for an explanation of whether the compiler should technically accept your program according to the standard, rather than a practical solution to the problem, you may want to tag the question `language-lawyer`. – user17732522 Feb 05 '22 at 15:42
  • @WhozCraig https://eel.is/c++draft/temp#inst-3.1 and https://eel.is/c++draft/temp#inst-5 say that the definition of a friend function defined in a class template is instantiated only if it is used in a context requiring a definition of the function or if the semantics of the program would depend on the existence of the definition. This doesn't seem like it should apply in the example by OP. – user17732522 Feb 05 '22 at 15:48
  • @HolyBlackCat Yes you're correct. – Jason Feb 05 '22 at 15:51
  • 1
    Dup of [A template friend function inside a template class](https://stackoverflow.com/questions/59415978/a-template-friend-function-inside-a-template-class) – Language Lawyer Feb 05 '22 at 19:21

3 Answers3

0

The free function

friend void anotherFeed(int x){}

is not dependent on the template parameter, hence, there is only this one free function which is defined twice in your example. Make it a forward declaration only

template<typename T>
struct Name {
    friend void anotherFeed(int x);
};

and define it outside the class definition

void anotherFeed(int x) {}

and all's well.

Ted Lyngmo
  • 93,841
  • 5
  • 60
  • 108
  • Bumped. Interestingly, while this totally works, you do lose one thing from the originally broken architecture: access to T in the friended function. Not that it would be particularly useful, considering you only get it once anyway. – WhozCraig Feb 05 '22 at 13:25
  • @WhozCraig Yes, I assume the question is purely academic since it's a pointless `friend` to make to begin with. – Ted Lyngmo Feb 05 '22 at 13:28
  • @TedLyngmo Thankyou for the answer. But i already knew how to solve the problem as you suggested by forward declaring and then defining it afterward. As you said this is for academic purposes. I read in a book that the definitions are only instantiated when used. So here we are not using the function and so no definition should be instantiated, but it still gives error. – Jason Feb 05 '22 at 13:42
  • @TedLyngmo You wrote: *"there is only this one free function which is **defined twice**"*. I understand this point of yours already. My point is that the actual definition will not be instantiated until we use the function `anotherFeed(int)`. And as in my program since we have not used `anotherFeed(int)` so there are no 2 actual definition about which we can say that we have redefinition. That is, when i write `s.anotherFeed(4); p.anotherFeed(5); //this second statement should give the redefinition error ` then only we should get the redefinition error. – Jason Feb 05 '22 at 14:19
  • @Anya _"I read in a book that the definitions are only instantiated when used"_ - `anotherFeed` is a free non-templated function. It doesn't get instantiated by using it. It's there (but can be optimized away if not used) no matter how many instances of the class template `Name` you create. – Ted Lyngmo Feb 05 '22 at 14:31
  • @TedLyngmo Ok so say we have `template struct Name { void feed(int x)//note no friend here { } }; int main() { Name p;//will this statement instantiate a definition of feed(int)? } `So when i wrote Name p above, will `feed`'s definition be instantiated? [Demo](https://onlinegdb.com/EmfQab6Sf) – Jason Feb 05 '22 at 14:56
  • @Anya `void Name::feed(int x)` is (if followed by a `;`) a **member** function _declaration_ . It won't be defined unless you add `template void Name::feed(int x) {}` outside the class and then instantiate a `Name` type, like `Name p;` - The function in your Demo is a class template member function definition that _will_ be instantiated following the same rule, when you instantiate a `Name` type, like `Name p;` – Ted Lyngmo Feb 05 '22 at 15:07
  • @TedLyngmo Ok so if we have say: `template struct Name { void feed(int x);//this is declaration }; //this is definition template void Name::feed(int x) { } int main() { Name p;//will this statement instantiate a definition of feed(int)? } ` Now are you saying that in this case the statement `Name p;` will instantiate the definition of `feed(int x);`? [Modified Demo](https://onlinegdb.com/WIBb1zizr) – Jason Feb 05 '22 at 15:14
  • @Anya _"Now are you saying..."_ - Yes, and that does not condratict anything else I've said. You could make that `Name p, q, r;` and it'd still be ok because of the implicit `inline`ing - which is _not_ what you have in your question in which you be`friend` a non-templated free function. – Ted Lyngmo Feb 05 '22 at 15:28
  • @TedLyngmo I quote from C++ Primer 5th edition, *"By default, a member function of a class template is instantiated only if the program uses that member function.If a member function isn’t used, it is not instantiated."* So i think you're contradicting the above quoted statement. According to C++ Primer [this](https://onlinegdb.com/f52QjL-iU) should not instantiate the definition of `feed` member function but you're saying that it does instantiate the definition. Similarly,I have read the same thing in other books.I mentioned one for now. Sorry for bothering you but i want to clear my concept. – Jason Feb 05 '22 at 15:36
  • @Anya Yes, true, I was wrong about that comment (_"The function in your Demo is a class template member function definition that will be instantiated following the same rule, when you instantiate a Name type, like Name p;"_). Again, this is not what you have in the question though so I don't see reasons for revising the actual answer to your question. – Ted Lyngmo Feb 05 '22 at 15:46
  • @TedLyngmo The friend definition in OP's code is still a templated entity (https://eel.is/c++draft/temp#pre-note-6) and https://eel.is/c++draft/temp#inst-3.1 and https://eel.is/c++draft/temp#inst-5 say that the definition of a friend function defined in a class template will be instantiated only if the function is used in a context requiring the definition or where existence of the definition would affect semantics of the program. I don't see this being the case here. – user17732522 Feb 05 '22 at 15:56
  • @user17732522 Yes, it's a templated entity and in OP:s code, the exact same non-inlined function is instantiated twice. – Ted Lyngmo Feb 05 '22 at 16:15
  • @TedLyngmo Only its declaration is instantiated (twice), the definition isn't instantiated. Redeclaration shouldn't be an error. – user17732522 Feb 05 '22 at 16:16
  • @user17732522 OP:s function definition includes the declaration. It is defined twice - and no, the multiple declarations is not a problem. – Ted Lyngmo Feb 05 '22 at 16:23
  • @TedLyngmo Here is an example of what I mean: https://godbolt.org/z/hK9vcPMvb This program is well-formed. The declaration of the friend function is instantiated, but the definition isn't. If it was, the program would be ill-formed, because variables cannot have type `void`. The same behavior should apply to the code in the question, assuming there isn't any specific rule against it. – user17732522 Feb 05 '22 at 16:33
  • @user17732522 Your function it's now declaring a local variable of type `T` which makes the function only usable for one `T` . Even though the function is defined in your example, it's only defined _once_. I do not know exactly what rules that applies in that situation but the compilers you used don't even reach that problem when the function is defined multiple times (redefinition error): [demo](https://godbolt.org/z/557rjeMWa). Remove `T x;` and it's still the same problem so I think it's simpler to leave `T x;` out. – Ted Lyngmo Feb 05 '22 at 20:20
0

For a function template, its declaration can be instantiated without instantiating its definition.

This instantiates everything except the function body (more or less).

The declaration of your function is instantiated when the class is instantiated. When (and if) you actually call the function, its definition is instantiated.

I can't find the right standard quote, but apparently the compiler doesn't need to instantiate the definitions to reject the duplicate instantiated declarations.

HolyBlackCat
  • 78,603
  • 9
  • 131
  • 207
  • Yes this is exactly what i was asking. That is,I knew that the declarations are instantiated without definitions and only when we use the function the definition will be instantiated The last sentence of your answer partly answer my question: *"compiler doesn't need to instantiate the definitions to reject the duplicate instantiated declarations"* Now i just need to see/read from some official documentation where itsays that compiler can reject this.Also,since we can have multiple declarations for the same function so compiler shouldn't giveerror if it has multiple declarations ofthe function. – Jason Feb 05 '22 at 14:42
0

This issue is addressed here.

However, for the purpose of determining whether an instantiated redeclaration is valid according to [basic.def.odr] and [class.mem], a declaration that corresponds to a definition in the template is considered to be a definition.

So even though there is no actual instantiation of the definition of anotherFeed(int) in your program, the compiler is still required to diagnose multiple definitions within the same translation unit as if the definition had been instantiated every time the declaration were instantiated.

And so when you wrote:

Name<double> p; //instantiate a declaration of anotherFeed(int) for the second time. 

The above statement, instantiate a redeclaration of anotherFeed(int) and since this declaration corresponds to a definition, according to the quoted statement at the beginning of my answer you get the redefinition error.

Jason
  • 36,170
  • 5
  • 26
  • 60