holox
holox

Reputation: 45

How to use a const reference as template parameter?

I am trying to implement a kind of matrix wrapper that takes a container of a container as template parameter.

But I get an error when trying to construct a matrix_wrapper of a const reference. The code seems to work with a non reference, non pointer, not const parameter and I would like to use the same code for both cases. Even though I'll have another template specialization for pointers.

When I try to compile this code I get the following errors:

>c:\users\emedeiros\source\repos\test\test\test.cpp(240): error C2079: 'matrix' uses undefined class 'matrix_wrapper<const std::vector<std::vector<double,std::allocator<_Ty>>,std::allocator<std::vector<_Ty,std::allocator<_Ty>>>> &>'
1>        with
1>        [
1>            _Ty=double
1>        ]
1>c:\users\emedeiros\source\repos\test\test\test.cpp(240): error C2440: 'initializing': cannot convert from 'const std::vector<std::vector<double,std::allocator<_Ty>>,std::allocator<std::vector<_Ty,std::allocator<_Ty>>>>' to 'int'
1>        with
1>        [
1>            _Ty=double
1>        ]
1>c:\users\emedeiros\source\repos\test\test\test.cpp(240): note: No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called
1

Below you'll find the class definition and a function that will try to create a matrix_wrapper.

#include <vector>

template <class T>
class matrix_wrapper;

template <typename T, class A1, class A2, template <typename, typename> class Cont1, template <typename, typename> class Cont2>
class matrix_wrapper < Cont2 < Cont1 < T, A1>, A2> >
{
public:
    typedef typename boost::call_traits<Cont2<Cont1<T, A1>, A2>>::value_type value_type;
    typedef typename boost::call_traits<value_type>::param_type param_type;
    typedef typename boost::call_traits<value_type>::reference reference;

    typedef Cont1<T, A1> vector_type;
    typedef typename boost::call_traits<vector_type>::reference vector_type_ref;
    typedef typename boost::call_traits<vector_type>::const_reference vector_type_const_ref;
    typedef T data_type;

    matrix_wrapper(reference data) : m_data(data) {}

    inline vector_type_const_ref operator[](size_t i) const
    {
        return m_data[i];
    }

    inline vector_type_ref operator[](size_t i)
    {
        return m_data[i];
    }

    inline reference data()
    {
        return m_data;
    }

protected:
    reference m_data;
};


void test(const std::vector<std::vector<double>>& data)
{
    matrix_wrapper<const std::vector<std::vector<double>>&> matrix(data);
}


int main()
{
    std::vector<std::vector<int>> v(10, std::vector<int>(10, 1));
    test(v);
}

What do I have to change so I can use a const reference as parameter?

Upvotes: 1

Views: 976

Answers (1)

Mikhail
Mikhail

Reputation: 21749

I won't directly answer your question, but I dare to guess what you really need to hear :)

First, having references as types of templates is not usually a good idea. If you want to make a wrapper, it makes sense to have a reference on a wrapped inside your class, but it is still better to keep template parameter type to be a value type, not a reference type. Having said that, if you are really sure you want to have reference type as a parameter, you might want to play with std::decay.

Then, I see you have partial template specialization here. From the code you posted it is not clear if you actually need it or not. I personally love to keep things simple, so I'd suggest you're good without it. In this case just parametrize your class on the one and only type T.

As a side note, do not declare your functions inline. Compiler knows best for you. And methods defined in scope of a class are inline by default anyway. Forget that inline means "please make this code faster". This actually means "this symbol might appear in several translation units, please pick a single definition for me".

Finally, boost is a great library, but I don't see how you need it here. All standard containers provide all the necessary type aliases in them, just ask.

Here is the compiling code simplified according to my comments:

#include <vector>

template <class T>
class matrix_wrapper
{
public:
  using reference = const T&;
  using vector_type_ref = typename T::reference;
  using vector_type_const_ref = typename T::const_reference;

  matrix_wrapper(reference data) : m_data(data) {}

  vector_type_const_ref operator[](size_t i) const
  {
    return m_data[i];
  }
  // BTW this won't compile for non-const objects, since you store a const
  // reference to the container, but that's a different story
  vector_type_ref operator[](size_t i) 
  {
    return m_data[i];
  }
  reference data()
  {
    return m_data;
  }

protected:
    reference m_data;
};


void test(const std::vector<std::vector<int>>& data)
{
    matrix_wrapper<std::vector<std::vector<int>>> matrix(data);
}

int main()
{
    std::vector<std::vector<int>> v(10, std::vector<int>(10, 1));
    test(v);
}

See live demo here: https://wandbox.org/permlink/tmemloS6wCHZlhNY

I see that you declared v in main() as a vector of ints, but you accept a vector of double in test(). I assumed this is a misprint and fixed the types.

I completely agree with @NathanOliver about 1D/2D thing, but that's again a different story.

Upvotes: 1

Related Questions