2

Consider the following example code for overloading the operator<< for a class A:

#include <iostream>
class A {
    template <typename T>
    friend A &operator<<(A &a, const T &t)
    {
         std::cout << t << std::endl;
         return a;
    }
    friend A &operator<<(A &a, const std::string &t)
    {
         return operator<<<std::string>(a, t + "x");
    }
};

My intention is that the second operator explicitly calls the first one.

However, in g++ 7.4 this fails with

In function 'A& operator<<(A&, const string&)':
     error: 'operator<<' not defined
        return operator<<<std::string>(a, t + "x");
                                ^~
     error: expected primary-expression before '>' token
        return operator<<<std::string>(a, t + "x");
                                                      ^

I however do not see why this should not compile.

Here is the code in godbolt.

Mooing Duck
  • 64,318
  • 19
  • 100
  • 158
HerpDerpington
  • 3,751
  • 4
  • 27
  • 43
  • 1
    maybe `return A::operator<< (a, t + "x");` ? note space << <> – metablaster Nov 08 '19 at 01:00
  • 1
    @metablaster That fails with _error: 'operator<<' is not a member of 'A'_ – HerpDerpington Nov 08 '19 at 01:02
  • 1
    [Clang has similar results](http://coliru.stacked-crooked.com/a/388a98acf44538c1) `"error: use of undeclared 'operator<<'"` `"error: expected '(' for function-style cast or type construction"`. It's almost like it thinks `operator<<` is a _type_ – Mooing Duck Nov 08 '19 at 01:14

3 Answers3

2

In-class friend functions are not exposed to any scope. Friend injection used to be a thing (before ADL was invented), but now there is no way to call them except with ADL unless you declare them beforehand. In this case, a workaround is to declare the template function outside the class beforehand.

class A;

template <typename T>
A &operator<<(A &a, const T &t);
L. F.
  • 19,445
  • 8
  • 48
  • 82
1

Call a function instead of calling an operator.

#include <iostream>
class A {
    template <typename T>
    static A &print(A &a, const T &t)
    {
        std::cout << t << std::endl;
        return a;
    }

    template <typename T>
    friend A &operator<<(A &a, const T &t)
    {
         return print(a, t);
    }
    friend A &operator<<(A &a, const std::string &t)
    {
         return print(a, t + "x");
    }
};
Indiana Kernick
  • 5,041
  • 2
  • 20
  • 50
1

You seem to wish to specialize your template function, but you're not doing it quite right. It should look more like this:

template <> friend A& operator<< <std::string>(A &a, const std::string &t)
{
    // Print in here some how. It's not exactly clear to me how you intend to
    // do this, as doing something like a << t will create infinite recursion

    // finally, return a
    return a;
}

Your other option is to switch the order of the functions, creating the template function after you create your first function:

friend A &operator<<(A &a, const std::string &t)
{
    // Again, still not sure what you want to do here
    // I just want to stress again though, don't do something
    // like a << t, or operator<<(a,t)
    // That will crash hard and fast, as there is no way to resolve
    // it. It will create infinite recursion

    return a;
}

template <typename T>
friend A &operator<<(A &a, const T &t)
{
     std::cout << t << std::endl;
     return a;
}

My intention is that the second operator explicitly calls the first one.

So, first, you will need to actually need the first option in that case.

Second, to do this, you will need to choose a type for t. You would do it like this:

operator<< <SomeType>(a,t);

Keep in mind, t will need to be implicitly converted to SomeType. Else, SomeType needs to be created by calling its constructor:

operator<< <SomeType>(a,SomeType(/* parameters to construct a SomeType ... */));

Note: Doing something like operator<< <SomeType>(a,t + "x") will always become infinitely recursive, and ultimately crash. This is because t + "x" is always an std::string. That means the compiler will always call this overload of the function infinitely, until it finally crashes from a stack overflow. So don't do that.

  • 1
    Specializing a template function is rare these days. Overloading is more flexible. And what’s the point of switching the order ? – L. F. Nov 08 '19 at 11:44
  • 1
    Switching the order would make it so it is no longer a specialization, it could use normal syntax for function declarations, not specialization syntax. But this doesn't allow the OP to call the "other function" as they said they wanted to. –  Nov 08 '19 at 15:13
  • The reason why it would not have caused an overflow is because it was not a specialization. It would have called the other operator function because there was an explicit template argument, which the function itself does not have. – HerpDerpington Nov 08 '19 at 16:03
  • @Herp I don't understand. It would cause an overflow because it would call itself infinitely regardless if it was a specialization. –  Nov 08 '19 at 16:10
  • @Chipster But it is not a specialization without `template <>`. Without this, the function is only an overload. And `operator<<` cannot refer to the overload, but must refer to the templated operator, which does the actual writing. Since I went with the solution proposed by @Kerndog73 below, I can actually use a specialization (or not; that does not matter in this case). – HerpDerpington Nov 08 '19 at 16:12
  • @Herp. Hmm, I'll have to think about that some more. Anyway, if another answer solved your problem, you can mark this question as solved by accepting that answer. To accept an answer, click the check mark next to the answer. –  Nov 08 '19 at 16:17
  • @Chipster I have noticed an additional problem that your answer solves. For "simple" types like `std::string` there might be an "ambiguous overload" problem, one candidate is the templated and the other the fixed type version of the function or the function that appears first will be called. This might only be solvable by specialization. The reason why I did not see this is because in my actual code `T` is the "ostream manipulator type" which the template arguments do not accept (see [here](https://stackoverflow.com/questions/1134388/stdendl-is-of-unknown-type-when-overloading-operator)). – HerpDerpington Nov 08 '19 at 16:27
  • @Chipster No, it is an overload. It is not a specialization. A specialization always has the `<>` part. – L. F. Nov 09 '19 at 00:10