【问题标题】:undefined reference to `operator<<(std::ostream&, /* class with non-type template parameters */&)'未定义对 `operator<<(std::ostream&, /* 具有非类型模板参数的类 */&)' 的引用
【发布时间】:2020-04-16 18:52:51
【问题描述】:

对于家庭作业,我必须使用非类型模板参数创建一个类,然后将std::(i/o)stream 运算符添加到它。但是,当我尝试编译 clang++ 时会出现链接器错误:

$ clang++ -o foo ./*.cpp -std=c++11 -Wall -Wextra -Wpedantic -Wconversion -Wnon-virtual-dtor
/tmp/16_15-8cda65.o: In function `main':
main.cpp:(.text+0x108): undefined reference to `operator<<(std::ostream&, Screen<9ul, 9ul> const&)'
clang: error: linker command failed with exit code 1 (use -v to see invocation)

我知道模板声明和定义必须在同一个翻译单元中,这里有很多问题和答案都指出了这一点。

我的删减代码如下:

main.cpp:

#include <iostream>

#include "Screen.h"

int main()
{
    Screen<9,9> smile =
    {
        {0,0,0,1,1,1,0,0,0},
        {0,1,1,0,0,0,1,1,0},
        {0,1,0,0,0,0,0,1,0},
        {1,0,0,1,0,1,0,0,1},
        {1,0,0,0,0,0,0,0,1},
        {1,0,1,0,0,0,1,0,1},
        {0,1,0,1,1,1,0,1,0},
        {0,1,1,0,0,0,1,1,0},
        {0,0,0,1,1,1,0,0,0}
    };

    std::cout << smile;

    return 0;
}

屏幕.h:

#ifndef SCREEN_H
#define SCREEN_H

#include <iostream>
#include <array>
#include <initializer_list>
#include <cstddef>

template <std::size_t W, std::size_t H>
class Screen
{
    /////////////
    // FRIENDS //
    /////////////

    friend std::ostream& operator<<(std::ostream&, const Screen<W,H>&);

    public:

        // declarations of ctors, public members, etc.

    private:

        //////////
        // DATA //
        //////////

        std::array<std::array<bool,W>,H> pixels;
};

/////////////////
// NON-MEMBERS //
/////////////////

// ostream operator
template <std::size_t W, std::size_t H>
std::ostream& operator<<(std::ostream&, const Screen<W,H>&);

#include "Screen_impl.h"
#endif

Screen_impl.h:

#ifndef SCREEN_IMPL_H
#define SCREEN_IMPL_H

#include <iostream>
#include <array>
#include <algorithm>
#include <stdexcept>
#include <initializer_list>
#include <cstddef>

// definitions...

/////////////////
// NON-MEMBERS //
/////////////////

// ostream operator
template <std::size_t W, std::size_t H>
std::ostream& operator<<(std::ostream& lhs, const Screen<W,H>& rhs)
{
    for (auto y = rhs.pixels.cbegin(); y < rhs.pixels.cend(); ++y)
    {
        for (auto x = y->cbegin(); x < y->cend(); ++x)
        {
            if (*x)
                lhs << '#';
            else
                lhs << ' ';
        }

        lhs << std::endl;
    }

    return lhs;
}

#endif

【问题讨论】:

  • 不太可能,因为我在这里没有看到明确的模板实例化。
  • @Michael 在int main() 之前插入template std::ostream&amp; operator&lt;&lt;(std::ostream&amp;, const Screen&lt;9,9&gt;&amp;); 会产生编译错误,表明该运算符不是Screen&lt;9,9&gt; 的朋友

标签: c++ templates linker-errors friend-function


【解决方案1】:

在类中声明的operator&lt;&lt; 函数不是函数模板,但稍后您将定义一个函数模板。因此,您声明的实体与您定义的实体不同。

你需要在类中声明函数模板为

template <std::size_t WX, std::size_t HX>
friend std::ostream& operator<<(std::ostream&, const Screen<WX, HX>&);

请注意,模板参数的命名需要与类模板参数不同,以避免出现阴影。

你也可以在类中定义operator&lt;&lt;

【讨论】:

    【解决方案2】:

    这位朋友运营商

    template <std::size_t W, std::size_t H>
    class Screen
    {
        /////////////
        // FRIENDS //
        /////////////
    
        friend std::ostream& operator<<(std::ostream&, const Screen<W,H>&);
        //
    

    不是模板运算符。

    所以它作为模板运算符的定义是无效的。也就是说它不是类定义中声明的非模板友元运算符的定义。

    因此编译器会发出一个错误,即找不到运算符的定义。

    将友元运算符的定义放在类定义中。在这种情况下,这个定义将用于类的每个特化。

    否则,您将需要分别为类的每个具体特化定义运算符。

    这是一个演示程序。

    #include <iostream>
    #include <iterator>
    #include <numeric>
    
    template <size_t N>
    class Array
    {
    private:
        int a[N];
    
    public:
        Array() 
        {
            std::iota( std::begin( a ), std::end( a ), 0 );
        }
    
        friend std::ostream & operator <<( std::ostream &os, const Array &a )
        {
            for ( const auto &item : a.a ) os <<item << ' ';
            return os;
        }
    };
    
    int main() 
    {
        std::cout << Array<1>() << '\n';
        std::cout << Array<2>() << '\n';
        std::cout << Array<3>() << '\n';
        std::cout << Array<4>() << '\n';
        std::cout << Array<5>() << '\n';
    
        return 0;
    }
    

    它的输出是

    0 
    0 1 
    0 1 2 
    0 1 2 3 
    0 1 2 3 4 
    

    【讨论】:

      猜你喜欢
      • 2021-04-20
      • 2021-04-13
      • 2020-03-28
      • 2013-07-28
      • 1970-01-01
      • 1970-01-01
      • 2018-01-26
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多