dan9er
dan9er

Reputation: 389

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

Upvotes: 0

Views: 1967

Answers (2)

Vlad from Moscow
Vlad from Moscow

Reputation: 311028

This friend operator

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

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

is not a template operator.

So its definition as template operator is invalid. That is it is not a definition of the non-template friend operator declared in the class definition.

As a result the compiler issues an error that the definition of the operator is not found.

Place the definition of the friend operator in the class definition. In this case this definition will be used for each specialization of the class.

Otherwise you will need to define the operator for each concrete specialization of the class separately.

Here is a demonstrative program.

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

Its output is

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

Upvotes: 1

cigien
cigien

Reputation: 60228

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.

Upvotes: 3

Related Questions