【问题标题】:How to explicity call a templated overload of operator <<?如何显式调用运算符<<的模板化重载?
【发布时间】:2020-03-04 15:16:01
【问题描述】:

考虑以下将operator&lt;&lt; 重载为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");
    }
};

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

但是,在 g++ 7.4 中,这会失败

在函数'A&运算符 错误:'操作员 返回运算符(a, t + "x");
^~
错误:“>”标记之前的预期主表达式
返回运算符(a, t + "x");
^

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

Here is the code in godbolt.

【问题讨论】:

  • 也许return A::operator&lt;&lt; &lt;std::string&gt;(a, t + "x"); ?音符空间
  • @metablaster 失败并出现 错误:'operator
  • Clang has similar results"error: use of undeclared 'operator&lt;&lt;'""error: expected '(' for function-style cast or type construction"。几乎就像它认为 operator&lt;&lt;&lt;std::string&gt; 是一个 type

标签: c++ g++ operator-overloading specifications


【解决方案1】:

您似乎希望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;
}

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

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

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

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

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

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

注意:像operator&lt;&lt; &lt;SomeType&gt;(a,t + "x") 这样的操作总是会变成infinitely recursive,并最终崩溃。这是因为t + "x" 始终是std::string。这意味着编译器总是会无限调用这个函数的重载,直到它最终从stack overflow 崩溃。所以不要那样做。

【讨论】:

  • 现在很少使用模板函数。重载更灵活。切换顺序又有什么意义呢?
  • 切换顺序会使它不再是特化,它可以使用函数声明的普通语法,而不是特化语法。但这不允许 OP 像他们所说的那样调用“其他函数”。
  • 之所以不会导致溢出是因为它不是专业化的。它会调用另一个运算符函数,因为有一个显式的模板参数,而函数本身没有。
  • @Herp 我不明白。它会导致溢出,因为无论它是否是特化,它都会无限调用自己。
  • @Chipster 但如果没有template &lt;&gt;,它就不是专业化的。没有这个,函数只是一个重载。而operator&lt;&lt;&lt;std::string&gt; 不能指代重载,而必须指代模板化操作符,由模板化操作符进行实际编写。由于我采用了下面@Kerndog73 提出的解决方案,因此我实际上可以使用专业化(或不使用;在这种情况下无关紧要)。
【解决方案2】:

类内友元函数不暴露于任何范围。 Friend 注入曾经是一件事(在 ADL 被发明之前),但现在除了使用 ADL 之外没有其他方法可以调用它们,除非您事先声明它们。在这种情况下,一种解决方法是事先在类之外声明模板函数。

class A;

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

【讨论】:

    【解决方案3】:

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

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

    【讨论】:

    • 这就是我最终实现课程的方式。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-03-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多