Search code examples
c++templateslinker-errorsfriend-function

undefined reference to `operator<<(std::ostream&, /* class with non-type template parameters */&)'


For a homework assignment, I have to make a class with non-type template parameters and then add std::(i/o)stream operators to it. However when I try to compile clang++ gives a linker error:

$ 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)

I am aware that template declarations and definitions have to be in the same translation unit, and there are plenty of questions & answers here that point that out.

My abridged code is as follows:

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

Screen.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

Solution

  • The operator<< function declared inside the class is not a function-template, but later you are defining a function-template. So you're declaring a different entity than what you are defining.

    You need to declare the function template inside the class as

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

    Note that the template parameters need to be named differently from the class template parameters, to avoid shadowing.

    You could also just define the operator<< inside the class.