问题在于使用模板编写运算符重载<<和>>

The problem is writing operator overloading << and >> using templates

提问人:keinpop 提问时间:11/1/2023 更新时间:11/1/2023 访问量:85

问:

我有一类形状,它的工作原理是形状由点(x 和 y 坐标)组成。但是重载输入和输出运算符时,我遇到了一个可能作为错误的问题。

编译器是这样说的:

In file included from main.cpp:1:
./header/figure.h:17:82: warning: friend declaration ‘std::ostream& operator<<(std::ostream&, const Figure<T>&)’ declares a non-template function [-Wnon-template-friend]
   17 |     friend std::ostream & operator<<(std::ostream & stream, const Figure<T> & fig);
      |                                                                                  ^
./header/figure.h:17:82: note: (if this is not what you intended, make sure the function template has already been declared and add ‘<>’ after the function name here)
./header/figure.h:18:76: warning: friend declaration ‘std::istream& operator>>(std::istream&, Figure<T>&)’ declares a non-template function [-Wnon-template-friend]
   18 |     friend std::istream & operator>>(std::istream & stream, Figure<T> & fig);
      |                                                                            ^
/usr/bin/ld: /tmp/ccVeFfI2.o: в функции «main»:
main.cpp:(.text+0xdb): неопределённая ссылка на «operator<<(std::ostream&, Figure<double> const&)»
/usr/bin/ld: main.cpp:(.text+0xf1): неопределённая ссылка на «operator>>(std::istream&, Figure<double>&)»
collect2: error: ld returned 1 exit status

figure.h

#pragma once

#include <ostream>

#include "./dynamicArray.h"

template <class T>
class Figure
{
public:
    Figure();
    Figure(const DArray<std::pair<T, T>> & points);
    Figure(const std::initializer_list<std::pair<T, T>> & coord);

    ~Figure() noexcept;

    friend std::ostream & operator<<(std::ostream & stream, const Figure<T> & fig);
    friend std::istream & operator>>(std::istream & stream, Figure<T> & fig);

protected:
    DArray<std::pair<T, T>> _points;
    std::string _name = "unnamed";
};

#include "../src/figure.cpp"

figure.cpp

#include "../header/figure.h"

template <class T>
Figure<T>::Figure() :
    _points() {}

template <class T>
Figure<T>::Figure(const DArray<std::pair<T, T>> & points) :
    _points(points) {}

template <class T>
Figure<T>::Figure(const std::initializer_list<std::pair<T, T>> & coord) : 
    _points(coord) {}

template <class T>
Figure<T>::~Figure() noexcept
{
}

template <class T>
std::ostream & operator<<(std::ostream & stream, const Figure<T> & fig)
{
    size_t size = fig._points.getSize();

    if (size == 0) {
        return stream << 0;
    }

    for (size_t i = 0; i < size; ++i) {
        stream << "Point " << i + 1 << "| ";
        stream << fig._points[i];
    }

    return stream;
}

template <class T>
std::istream & operator>>(std::istream & stream, Figure<T> & fig)
{
    std::pair<T, T> tmp;
    std::cout << "Enter Ox: ";
    stream >> tmp.first;
    std::cout << "Enter Ox: ";
    stream >> tmp.second;

    fig._points.pushBack(tmp);

    return stream;
}
C++ 模板 operator-重载 ostream istream

评论


答:

0赞 Some programmer dude 11/1/2023 #1

宣言

friend std::ostream & operator<<(std::ostream & stream, const Figure<T> & fig);

不是模板声明。但

template <class T>
std::ostream & operator<<(std::ostream & stream, const Figure<T> & fig)

是一个模板。

您需要从一开始就将函数声明为模板:

template<typename U>
friend std::ostream & operator<<(std::ostream & stream, const Figure<U> & fig);

另请阅读为什么模板只能在头文件中实现?

模板不能像这样真正拆分为单独的源文件和头文件。

评论

0赞 keinpop 11/1/2023
它没有帮助,我已经尝试过了,越来越多的警告出现。甚至还有一个错误:/usr/include/c++/11/ostream:750:5: error: no type named ‘type’ in ‘struct std::enable_if<false, void>’
1赞 chris 11/1/2023
@keinpop,不同的错误并不意味着它没有帮助。我注意到您正在尝试输出 .std::pair
0赞 Some programmer dude 11/1/2023
@keinpop 您询问的错误消息非常明确地指出声明“声明了非模板函数”。friend
0赞 keinpop 11/1/2023
@Someprogrammerdude,如果我删除朋友,会出现其他错误,这无济于事
1赞 Remy Lebeau 11/1/2023
@Someprogrammerdude “模板不能像那样真正拆分为单独的源文件和头文件”——如果你更仔细地看,是'ing在最后,所以这是分离模板声明和定义的有效方法,只要不编译为它自己的翻译单元。figure.h#includefigure.cppfigure.cpp
0赞 Mooing Duck 11/1/2023 #2

问题是 ,但后来你把它定义为一个模板函数。解决方案是首先简单地将其声明为模板函数。friend declaration ‘std::ostream& operator<<(std::ostream&, const Figure<T>&)’ declares a non-template function

template <class T>
friend std::ostream & operator<<(std::ostream & stream, const Figure<T> & fig);

template <class T>
friend std::istream & operator>>(std::istream & stream, Figure<T> & fig);

这感觉有点奇怪,宣布这些过载是朋友,但这是简单的解决方案。Figure<T>T

“更正确”的解决方案稍微棘手一些。语法在我的脑海中,可能略有错误,但应该是这样的

//declare the class as existing as a template type.
template <class T>
class Figure; 

//declare the function as existing as a template function.
template <class T>
std::ostream & operator<<(std::ostream & stream, const Figure<T> & fig);

//define the class
template<class T>
class Figure
{
public:
    ....
    
    //now you can friend the specific instantiations
    friend std::ostream & operator<<(std::ostream & stream, const Figure<T> & fig);
    friend std::istream & operator>>(std::istream & stream, Figure<T> & fig);

或者语法可能是什么的。我不确定。friend std::ostream & operator<< <T>(std::ostream & stream, const Figure<T> & fig)

0赞 Red.Wave 11/1/2023 #3

最简单的方法是内联友元函数。我们可以组合的另一件事是模板名称注入;即在模板中,您不需要模板参数列表,除非您引用具有不同参数集的另一个实例化:

template<typename T>
struct test{
    T val;
    friend auto& operator<<
    (std::ostream& os, test const& x){
          return os << "value=" << x.val << "\n";
    }; 
};

std::cout << test<int>{1} << test<std::string>{"hello"};

您可以从这个简单的示例开始,并对其进行详细说明以获得最终结果。