如何显式调用运算符 << 的模板化重载?

How to explicity call a templated overload of operator <<?

考虑以下为 class A 重载 operator<< 的示例代码:

#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");
    }
};

我的意图是第二个运算符显式调用第一个运算符。

然而,在 g++ 7.4 中,这失败了

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");
                                                      ^

但是我不明白为什么这不应该编译。

Here is the code in godbolt.

调用函数而不是调用运算符。

#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");
    }
};

In-class 友元函数 暴露给任何作用域。朋友注入曾经是一回事(在发明 ADL 之前),但现在除了使用 ADL 之外没有其他方法可以调用它们,除非您事先声明它们。在这种情况下,解决方法是事先在 class 之外声明模板函数。

class A;

template <typename T>
A &operator<<(A &a, const T &t);

您似乎希望 specialize your template function,但您做的并不完全正确。它应该看起来更像这样:

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;
}

您的另一个选择是切换函数的顺序,在您创建第一个函数之后创建模板函数:

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.

因此,首先,在这种情况下,您需要实际需要第一个选项。

其次,为此,您需要为 t 选择一种类型。你会这样做:

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

请记住,t 需要 implicitly convertedSomeType。否则,SomeType 需要通过调用其构造函数来创建:

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

注意:像operator<< <SomeType>(a,t + "x")这样的操作总是会变成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。所以不要那样做。