0

As explained in this previous question, I'm trying to be able to assign references to a vector in order to use stl algorithms over it and to reflect the changes into the original data.

#include <vector>
#include <iostream>
#include <algorithm>

using Matrix = std::vector<std::vector<int>>;

/**
 * Class implementing std::reference_wrapper that
 * cannot be rebound after creation.
 *
 **/
template <class T>
class single_bind_reference_wrapper {

    // pointer to the original element
    T *p_;

public: // typedefs

    using type = T;

    // construct/copy/destroy
    single_bind_reference_wrapper(T& ref) noexcept : p_(std::addressof(ref)) {}
    single_bind_reference_wrapper(T&&) = delete;

    // Enable implicit convertsion from ref<T> to ref<const T>,
    // or ref<Derived> to ref<Base>
    template <class U, std::enable_if_t<std::is_convertible<U*, T*>{}, int> = 0>
    single_bind_reference_wrapper(const single_bind_reference_wrapper<U>& other) noexcept :
        p_(&other.get()) { }

    // assignment
    template <class U>
    decltype(auto) operator=(U &&u) const 
          noexcept(noexcept(std::declval<T>() = std::forward<U>(u))) {
        return get() = std::forward<U>(u);
    }

    // access
    operator T& () const noexcept { return *p_; }
    T& get() const noexcept { return *p_; }
};

void rotate_mat (Matrix &mat, int r){
    auto m = mat.size(); // Number of rows
    auto n = mat[0].size(); // Number of columns
    auto n_rings = std::min(m,n)/2; // Number of rings
    for(auto ring_i=0; ring_i<n_rings; ++ring_i){
        // The elements of the ring are stored sequentially 
        // in v_ring so it can be rotated with std::rotate 
        std::vector<single_bind_reference_wrapper<int>>  v_ring;
        std::vector<int*> v_ring_ptr;
        // Top side of the ring
        for(auto j=ring_i; j<=(n-1)-ring_i; ++j) {
            v_ring.push_back(mat[ring_i][j]);
        }
        // Right side of the ring
        for(auto i=ring_i+1; i<=(m-1)-ring_i; ++i) {
            v_ring.push_back(mat[i][(n-1)-ring_i]);
        }
        // Bottom size of the ring
        for(auto j=(n-1)-ring_i-1; j>ring_i; --j) {
            v_ring.push_back(mat[(m-1)-ring_i][j]);
        }
        // Left size of the ring
        for(auto i=(m-1)-ring_i; i>ring_i; --i) {
            v_ring.push_back(mat[i][ring_i]);
        }

        v_ring[0] = 10; // compilation error!
        // This would be my goal:
        //std::rotate(v_ring.begin(),v_ring.begin()+r%v_ring.size(),v_ring.end());
    }
};

Matrix read_matrix(int m, int n) {
    Matrix mat;
    mat.reserve(m);
    for(auto i=0; i<m; ++i) {
        mat.push_back(std::vector<int>{});
        mat[i].reserve(n);
        for(auto j=0; j<n; ++j) {
            int x; std::cin >> x;
            mat[i].push_back(x);
        }
    }
    return mat;
};

void print_matrix(Matrix &mat){
    for (auto& i : mat){
        for (auto& j : i) {
            std::cout << j << " ";
        }
        std::cout << "\n";
    }
};

int main() {
    int m,n; std::cin >> m >> n;
    int r; std::cin >> r;

    auto mat = read_matrix(m,n);
    rotate_mat(mat,r);
    print_matrix(mat);

    return 0;
}

The goal is to be able to do std::rotate over the v_ring and to see its effect over the original elements of mat. Never the less, I'm still not able to implement the = operator properly.

The compilation error I get is:

solution.cc: In function ‘void rotate_mat(Matrix&, int)’:
solution.cc:72:21: warning: ISO C++ says that these are ambiguous, even though the worst conversion for the first is better than the worst conversion for the second:
         v_ring[0] = 10;
                     ^~
solution.cc:34:20: note: candidate 1: decltype(auto) single_bind_reference_wrapper<T>::operator=(U&&) const [with U = int; T = int]
     decltype(auto) operator=(U &&u) const
                    ^~~~~~~~
solution.cc:13:7: note: candidate 2: constexpr single_bind_reference_wrapper<int>& single_bind_reference_wrapper<int>::operator=(single_bind_reference_wrapper<int>&&)
 class single_bind_reference_wrapper {
       ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
solution.cc:72:21: warning: ISO C++ says that these are ambiguous, even though the worst conversion for the first is better than the worst conversion for the second:
         v_ring[0] = 10;
                     ^~
solution.cc:34:20: note: candidate 1: decltype(auto) single_bind_reference_wrapper<T>::operator=(U&&) const [with U = int; T = int]
     decltype(auto) operator=(U &&u) const
                    ^~~~~~~~
solution.cc:13:7: note: candidate 2: constexpr single_bind_reference_wrapper<int>& single_bind_reference_wrapper<int>::operator=(const single_bind_reference_wrapper<int>&)
 class single_bind_reference_wrapper {
       ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
solution.cc: In instantiation of ‘decltype(auto) single_bind_reference_wrapper<T>::operator=(U&&) const [with U = int; T = int]’:
solution.cc:72:21:   required from here
solution.cc:35:47: error: using xvalue (rvalue reference) as lvalue
           noexcept(noexcept(std::declval<T>() = std::forward<U>(u))) {
                             ~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~
solution.cc: In instantiation of ‘decltype(auto) single_bind_reference_wrapper<T>::operator=(U&&) const [with U = int; T = int]’:
solution.cc:72:21:   required from here
solution.cc:35:47: error: using xvalue (rvalue reference) as lvalue
Blasco
  • 1,607
  • 16
  • 30
  • A vector of vector is a bad idea for a matrix. Use a vector of size row*col. –  Jan 26 '18 at 11:03
  • 2
    The `noexcept(...)` issue can be fixed by switching to `std::is_nothrow_assignable` (see the updated linked answer), and the ambiguity can be resolved by adding `operator=(single_bind_reference_wrapper)` but I have to admit I don't know why this is ambiguous in the first place. – Holt Jan 26 '18 at 11:05

0 Answers0