There are quite some posts about this topic already, but neither proposed solution did help me to compile and/or link my code.
The general proposed solution to this is to forward declare the class, forward declare the operator/function, declare it as a friend and then implement it.
The code I try to compile is:
#include "matrix.hpp"
int main()
{
using namespace MLL;
Matrix<int, 4, 4> a({1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16});
a+a;
return 0;
}
Variant 1
#include <algorithm>
#include <array>
#include <type_traits>
#include <vector>
namespace MLL{
template<typename data_t, std::size_t n_rows, std::size_t n_cols, std::size_t MAX = 256>
class Matrix;
template<typename data_t, typename T, std::size_t n_rows, std::size_t n_cols, std::size_t MAX, std::size_t other_MAX>
Matrix<decltype(std::declval<data_t>() + std::declval<T>()), n_rows, n_cols, std::min(MAX, other_MAX)>
operator+(Matrix<data_t, n_rows, n_cols, MAX> const& lhs, Matrix<T, n_rows, n_cols, other_MAX> const& rhs);
template<typename data_t, std::size_t n_rows, std::size_t n_cols, std::size_t MAX>
class Matrix{
static constexpr bool IS_STATIC = n_rows * n_cols <= MAX;
using container_t = typename std::conditional<IS_STATIC, std::array<data_t, n_rows * n_cols>, std::vector<data_t>>::type;
container_t m_data_list;
public:
Matrix(){
if constexpr( !IS_STATIC ){
m_data_list.resize(n_rows * n_cols);
}
}
explicit Matrix(data_t default_value){
if constexpr( IS_STATIC ){
m_data_list.fill(default_value);
}else{
m_data_list.resize(n_rows * n_cols, default_value);
}
}
explicit Matrix(std::initializer_list<data_t>&& value_list){
std::copy(value_list.begin(), value_list.end(), m_data_list.begin());
}
Matrix(Matrix const& other)
: m_data_list(other.m_data_list){
}
Matrix(Matrix&& other) noexcept
: m_data_list(std::move(other.m_data_list)){
}
Matrix& operator=(Matrix const& other){
m_data_list = other.m_data_list;
return *this;
}
Matrix& operator=(Matrix&& other) noexcept{
m_data_list = std::move(other.m_data_list);
return *this;
}
template<typename T, std::size_t other_MAX>
friend Matrix<decltype(std::declval<data_t>() + std::declval<T>()), n_rows, n_cols, std::min(MAX, other_MAX)>
operator+(Matrix<data_t, n_rows, n_cols, MAX> const& lhs, Matrix<T, n_rows, n_cols, other_MAX> const& rhs);
};
template<typename data_t, typename T, std::size_t n_rows, std::size_t n_cols, std::size_t MAX, std::size_t other_MAX>
Matrix<decltype(std::declval<data_t>() + std::declval<T>()), n_rows, n_cols, std::min(MAX, other_MAX)>
operator+(Matrix<data_t, n_rows, n_cols, MAX> const& lhs, Matrix<T, n_rows, n_cols, other_MAX> const& rhs){
const std::size_t n = n_rows * n_cols;
for( std::size_t i = 0; i < n; ++i ){
lhs.m_data_list[i] += rhs.m_data_list[i];
}
return lhs;
}
}
Here the linker complains that the function is not implemented.
undefined reference to `MLL::Matrix<decltype (((std::declval<int>)())+((declval<int>)())), 4ull, 4ull, (std::min<unsigned long long>)(256ull, 256ull)> MLL::operator+<int, 256ull>(MLL::Matrix<int, 4ull, 4ull, 256ull> const&, MLL::Matrix<int, 4ull, 4ull, 256ull> const&)'
Variant 2
#include <algorithm>
#include <array>
#include <type_traits>
#include <vector>
namespace MLL{
template<typename data_t, std::size_t n_rows, std::size_t n_cols, std::size_t MAX = 256>
class Matrix;
template<typename data_t, typename T, std::size_t n_rows, std::size_t n_cols, std::size_t MAX, std::size_t other_MAX>
Matrix<decltype(std::declval<data_t>() + std::declval<T>()), n_rows, n_cols, std::min(MAX, other_MAX)>
operator+(Matrix<data_t, n_rows, n_cols, MAX> const& lhs, Matrix<T, n_rows, n_cols, other_MAX> const& rhs);
template<typename data_t, std::size_t n_rows, std::size_t n_cols, std::size_t MAX>
class Matrix{
static constexpr bool IS_STATIC = n_rows * n_cols <= MAX;
using container_t = typename std::conditional<IS_STATIC, std::array<data_t, n_rows * n_cols>, std::vector<data_t>>::type;
container_t m_data_list;
public:
Matrix(){
if constexpr( !IS_STATIC ){
m_data_list.resize(n_rows * n_cols);
}
}
explicit Matrix(data_t default_value){
if constexpr( IS_STATIC ){
m_data_list.fill(default_value);
}else{
m_data_list.resize(n_rows * n_cols, default_value);
}
}
explicit Matrix(std::initializer_list<data_t>&& value_list){
std::copy(value_list.begin(), value_list.end(), m_data_list.begin());
}
Matrix(Matrix const& other)
: m_data_list(other.m_data_list){
}
Matrix(Matrix&& other) noexcept
: m_data_list(std::move(other.m_data_list)){
}
Matrix& operator=(Matrix const& other){
m_data_list = other.m_data_list;
return *this;
}
Matrix& operator=(Matrix&& other) noexcept{
m_data_list = std::move(other.m_data_list);
return *this;
}
template<typename T, typename U, std::size_t m_rows, std::size_t m_cols, std::size_t this_MAX, std::size_t other_MAX>
friend Matrix<decltype(std::declval<T>() + std::declval<U>()), m_rows, m_cols, std::min(this_MAX, other_MAX)>
operator+(Matrix<T, m_rows, m_cols, this_MAX> const& lhs, Matrix<T, m_rows, m_cols, other_MAX> const& rhs);
};
template<typename data_t, typename T, std::size_t n_rows, std::size_t n_cols, std::size_t MAX, std::size_t other_MAX>
Matrix<decltype(std::declval<data_t>() + std::declval<T>()), n_rows, n_cols, std::min(MAX, other_MAX)>
operator+(Matrix<data_t, n_rows, n_cols, MAX> const& lhs, Matrix<T, n_rows, n_cols, other_MAX> const& rhs){
const std::size_t n = n_rows * n_cols;
for( std::size_t i = 0; i < n; ++i ){
lhs.m_data_list[i] += rhs.m_data_list[i];
}
return lhs;
}
}
Error 2
C:/Users/CLionProjects/MLL/include/matrix.hpp:68:17: error: 'MLL::Matrix<int, 4, 4>::container_t MLL::Matrix<int, 4, 4>::m_data_list' is private within this context
68 | lhs.m_data_list[i] += rhs.m_data_list[i];
| ~~~~^~~~~~~~~~~
C:/Users/CLionProjects/MLL/include/matrix.hpp:19:21: note: declared private here
19 | container_t m_data_list;
| ^~~~~~~~~~~
C:/Users/CLionProjects/MLL/include/matrix.hpp:68:39: error: 'MLL::Matrix<int, 4, 4>::container_t MLL::Matrix<int, 4, 4>::m_data_list' is private within this context
68 | lhs.m_data_list[i] += rhs.m_data_list[i];
| ~~~~^~~~~~~~~~~
C:/Users/CLionProjects/MLL/include/matrix.hpp:19:21: note: declared private here
19 | container_t m_data_list;
| ^~~~~~~~~~~h
C:/Users/CLionProjects/MLL/include/matrix.hpp:68:32: error: assignment of read-only location 'lhs.MLL::Matrix<int, 4, 4>::m_data_list.std::array<int, 16>::operator[](i)'
68 | lhs.m_data_list[i] += rhs.m_data_list[i];
| ~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~
ninja: build stopped: subcommand failed.
Question
I do not want to define it within the friend-declaration.
Can you please explain what I'm doing wrong in both cases, and how I can fix it?