2

I've just started learning templates in C++ and for practice purposes wrote this simple code

#include <iostream>

template<typename T>
class A;

template<typename T>
std::ostream& operator<<(std::ostream& out, A<T> x);

template <typename T>
class A
{
    T m_x = 10;
public:
    A(T x) : m_x{ x } {}
    friend std::ostream& operator<< <T>(std::ostream& out, A x);
};

template<typename T>
std::ostream& operator<<(std::ostream& out, A<T> x)
{
    out << "m_x = " << x.m_x;
    return out;
}


int main()
{
    A<int> a1{ 10 };
    std::cout << a1 << '\n';
}

It works as expected, that is I get 10 as the output, but there is one thing that bothers me. At which point is the operator<< function instantiated? Does it happen at the point of creation of the a1 object (this is also the point where A<int> is implicitly instatiated, right?) or does it happen when I call the operator<< in std::cout << a1 << '\n'? My guess is that the second option is correct and I base it on this excerpt from cppreference which says that:

When code refers to a function in context that requires the function definition to exist, or if the existence of the definition affects the semantics of the program (since C++11), and this particular function has not been explicitly instantiated, implicit instantiation occurs.

But is it true that a friend declaration does not require the function definition to exist?

I'm sorry if this question is ill-pharased, I did my best to use the nomenclature right, but I'm just a beginner.

EDIT

What about this?

template <typename T>
class foo
{
    T m_x;
    friend void bar(foo x)
    {
        x.m_x = "123";
    }
};

if I put a friend function definition inside a class, every instantiation of that class causes a new, ordinary function overload to be created that takes an argument of the current specialization, hence I would expect to see an error as soon as I write this: foo<int> x; but I don't get one... (for example, bar(x); causes the error)

AJB
  • 97
  • 1
  • 7

2 Answers2

0

I am not a language lawyer, so I'll try to answer with examples.

There are two aspects. First friend declaration.

A friend delcaration is a declaration. For example:

struct foo {
    friend void bar();
};

int main() {
    foo f;
}

Compiles and executes without any problem.

friend void bar(); declares a function bar. This functions is never defined. As long as we do not call it, thats not an issue. Of course, typically you would provide a definition, but if the function is never called you do not necessarily need to define it. Actually there are situations where having a declaration but no definition is on purpose, though thats out of scope of the quesiton.

Next, is templates and the question when member functions are instantiated. For that, consider this example:

#include <iostream>

template <typename T>
struct foo {
    void bar() {
        T x = "123";
    }
};

int main() {
    foo<int> x;
    //x.bar();// error!
}

Compiles and executes without any problem.

Initializing an int with the string literal "123" is of course non-sense. Its only to provoke a compiler error when the method is instantiated. We can create an object of foo<int>. Though, we cannot call foo<int>::bar, because its not valid.

You can find many examples of methods of class templates that can only conditionally be instantiated in the standard library. For example std::map<key_t,value_t>::operator[]. It requires value_t to be default constructible, because it potentially has to default construct a value_t. You can use a std::map with a non-default-constructible value_t, you just cannot call its operator[]:

#include <map>

struct foo {
    foo(int){}
};

int main() {
    std::map<int,foo> x;     // completely fine
    x[1] = foo(1);           // error
}

I spare you the complete error message. It is horrible, the essential part is:

 error: no matching function for call to 'foo::foo()'
463035818_is_not_an_ai
  • 109,796
  • 11
  • 89
  • 185
  • This explanation makes sense, it shows that a friend declaration isn't enough to cause implicit instantiation of a function template. But if I put a friend function **definition** inside a class, every instantiation of that class causes a new, ordinary function overload to be created that takes an argument of the current specialization, hence I would expect to see an error as soon as I write this: `foo x;` (see my edit of the question for foo definition) – AJB Apr 28 '21 at 19:21
  • @AJB well, that requires more than my handwaving to be explained properly. I only "know" that "every instantiation of that class causes a new, ordinary function overload to be created" is not correct, but I cannot explain you :/ Also note that your example is a little tricky, because you need a declaration outside the class to be able to call `bar` outside the class, are you sure the compiler error is not actually about that? – 463035818_is_not_an_ai Apr 28 '21 at 19:26
  • the portion of my comment which you quote above and state as incorrect is an excerpt of [this text](https://www.linuxtopia.org/online_books/programming_books/c++_practical_programming/c++_practical_programming_125.html) This seems to be the situation we are dealing with here (foo class template as defined in the edit to my question). This whole thing really perplexes me... – AJB Apr 28 '21 at 19:38
  • @AJB C++ is complicated, don't trust anybody when they cannot quote the standard :P. I am reading the text, could well be that I was wrong, though often online tutorials are just outright wrong or have or misleading information. Unfortunately thats rather common – 463035818_is_not_an_ai Apr 28 '21 at 19:42
  • @AJB i said "wrong" becuase I read "created" as "instantiated", but after reading the text I think they don't mean "instantiated", though I also don't know what would be a better word to describe what happens in their example. I have to admit that only now I completely understand the point of the quesiton and why my "answer" does not answer it – 463035818_is_not_an_ai Apr 28 '21 at 19:52
  • @AJB btw before reading i didnt know that it is an online version of a book, so my rant about online tutorials was rather misplaced ;) – 463035818_is_not_an_ai Apr 28 '21 at 19:58
  • @AJB in your original example the situation is rather clear, your `operator<<` is a template and only instantiated when you call it. The edit is more tricky because `bar` is no template, hence I am puzzled as well why instantiating `foo` is fine. Not sure what to do. If you don't mind I'll write a new `language-lawyer` question to clarify the issue – 463035818_is_not_an_ai Apr 28 '21 at 20:11
  • That would be great, and thank you for addressing the first part of my question. Just me know where I'll be able to find your "language-lawyer" question – AJB Apr 28 '21 at 20:29
  • @AJB here you go: https://stackoverflow.com/q/67307554/4117728. I didnt want to steal the quesiton from you, its just that I didnt see another way to clarify other than to ask for help from a language-lawyer (someone who knows how to read and interpret the standard, something that appears voodoo to me most of the time) and instead of suggesting you to rephrase your question it appeared simpler to write another one. – 463035818_is_not_an_ai Apr 28 '21 at 20:38
0

The overload of the operator<< in your case gives you an individual overload to every template classA<T>, so I can assume, that instantiation of operator<< can be with every instantiation of your class A<T>.

You can checkout this post, it can be useful in your case.

veekzee
  • 23
  • 8