Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

template deduction failed for std::plus

Tags:

The following code defines an operator+ between std::array of arbitrary type. The implementation consists in defining an auxiliary routine op2 that takes a generic callable operator and applies it to every element of the array, via template recursion.

#include <array>
#include <functional>
#include <iostream>

template <typename T, std::size_t N>
std::array<T, N> operator+(const std::array<T, N>& lhs, const std::array<T, N>& rhs);

template <std::size_t Index = 0, typename T, std::size_t N, class F>
void op2(const std::array<T, N>& lhs, const std::array<T, N>& rhs, std::array<T, N>& res, F&& op);

template <typename T, std::size_t N>
std::array<T, N> operator+(const std::array<T, N>& lhs, const std::array<T, N>& rhs)
{
    std::array<T, N> res;
    op2(lhs, rhs, res, std::plus{});
    return res;
}

template <std::size_t Index, typename T, std::size_t N, class F>
void op2(const std::array<T, N>& lhs, const std::array<T, N>& rhs, std::array<T, N>& res, F&& op)
{
    std::get<Index>(res) = op(std::get<Index>(lhs), std::get<Index>(rhs));
    if constexpr (Index < (N - 1))
        op2<Index + 1>(lhs, rhs, res, std::forward<F>(op));
}


template <std::size_t N>
void print_array(const std::array<int, N>& a)
{
    for (int x : a)
        std::cout << x << ' ';
    std::cout << '\n';
}

int main(){
    std::array<int, 3> a{1, 2, 3};
    std::array<int, 3> b{2, 2, 1};
    
    print_array(a + b);

    std::array<std::array<int, 3>, 2> c{a, b};
    std::array<std::array<int, 3>, 2> d{a, b};
    
    auto x = c + d; // Error: no match for call to std::plus<void>


    return 0;
}

See it Live on Coliru.

The first part works fine and computes the sum of the arrays of int a and b.

Next, c and d are std::array of std::array<int,3>. The program fails to compile because it cannot find a match for std::plus between components of c and d, that is, std::plus::operator() cannot understand how to sum two std::array<int, 3>.

A workaround is to define operator+ as

template <typename T, std::size_t N>
std::array<T, N> operator+(const std::array<T, N>& lhs, const std::array<T, N>& rhs)
{
    std::array<T, N> res;
    op2(lhs, rhs, res, [](const auto& lhs, const auto& rhs) { return lhs + rhs; });
    return res;
}

See it Live on Coliru.

Accordind to the reference std::plus=std::plus<void> should simply return the sum result with parameter and return type deduced, pretty much similar to what the anonymous lambda does.

Why the solution with std::plus does not compile?

like image 846
francesco Avatar asked Nov 11 '20 10:11

francesco


1 Answers

The same problem occurs in the following simplified demo:

std::array<int, 3> a{1, 2, 3};
std::array<int, 3> b{2, 2, 1};

print_array(std::plus{}(a, b));

Internally, std::plus invokes operator+. But since it is invoked in the std::namespace, it may not find your custom operator+ defined outside of this namespace.


Generally, operators for some class should be defined in the namespace where that class is defined. But in the case of std::array, this is not possible.

like image 93
Daniel Langr Avatar answered Sep 30 '22 16:09

Daniel Langr