Reputation: 389
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
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
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