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?
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.
Related
I know that trying to use a std::initializer_list<NonCopyable> leads to an error because the elements are copied into the temporary array represented by the initializer_list. I have also read some explanation on why it would not be alright to have rvalue references in the list, which I'm fine with.
The problem is that I would like to pass noncopyable things not in order to move from them, but only const-access them, so the argument about rvalues does not apply. What can I do to retain, if possible, the list initialization syntax and the reference semantics (no wrappers, no raw pointers)?
NonCopyable a{...}, b{...};
ListInitialized c{a, b};
I think I'm missing something extremely obvious here.
Update:
This works(*),
ListInitialized(std::initializer_list<std::reference_wrapper<NonCopyable>>) {...}
but won't accept rvalues. It would be nice if I could simply pass a list of anything that could go into const NonCopyable&.
(*) I know I wrote "no wrappers" but this affects neither the calling code nor the iteration over the list.
You can give ListInitialized a variadic constructor template:
struct ListInitialized
{
template <class... T>
ListInitialized(const T... &arg);
};
If you need to make sure it can only be instantiated with the correct type, consider suitable SFINAE:
struct ListInitialized
{
template <
class... T,
class Sfinae = std::enable_if_t<std::is_same<std::decay_t<T>, NonCopyable> &&...
>
ListInitialized(const T... &arg);
};
In addition to the comments and answers above, I found that this minimalistic wrapper fulfills my needs:
#include <initializer_list>
#include <utility>
struct S {
S() { }
S(const S&) = delete; // Non-copyable
void f() const { }
};
template<class T>
class const_reference_wrapper {
public:
const_reference_wrapper(const T& ref_) : ref(ref_) { }
operator const T&() const { return ref; }
private:
const T& ref;
};
struct T {
T(std::initializer_list<const_reference_wrapper<S>> l) : c(l.size()) {
for(const S& i : l) // note: const auto& can't be used here, but it would be the same for std::reference_wrapper
i.f(); // we can do something with the elements
}
int c;
};
int main() {
S a, b;
T t{a, b, S{}}; // we can mix lvalues and rvalues with a natural syntax
return t.c; // correctly returns 3
}
Of course, care needs to be taken to ensure that any rvalue passed through this will live through the time it is being referenced.
I have a struct that manages 'views' into an array of variable type. The purpose of this is to provide a unified state vector for ODE simulation while at the same time working on individual segments of this vector from several other classes in an organized fashion. If this triggers a design pattern in your mind, please let me know.
My issue is that the first implementation ContainerHolderFirst, using Cont::pointer does not compile for const arrays.
My next attempt with std::conditional, mixing in Cont::const_pointer still doesn't work.
Only the third attempt with std::conditional and modification of Cont::value_type compiles (and seems to work in my larger project).
My questions are the following:
It would be nice if ContainerHolderFirst would work. I'd wish that const-ness of the type would be propagated to the pointer. Why isn't it?
I understand even less why ContainerHolderSecond doesn't work. The explanation in https://stackoverflow.com/a/1647394/1707931 rather suggests that this is the way to go, no? It does actually work. My bad.
Are there issues with the third approach that I haven't uncovered yet? Is there a simpler way?
Full C++11 code follows:
Update1: Fixing ContainerHolderSecond. It does compile with correct initialization. Also added ContainerHolderBarry suggested by Barry using decltype and declval.
This leaves the question whether any of the approaches are preferred? Will they lead to performance differences? They should all compile to the same object, no?
#include <iostream>
#include <array>
template <typename Cont>
class ContainerHolderFirst {
Cont& data_;
const static size_t offset_ = 1;
typename Cont::pointer data_view;
public:
ContainerHolderFirst(Cont& data) : data_(data), data_view(&data[offset_]) {}
};
template <typename Cont>
class ContainerHolderSecond {
using Pointer = typename std::conditional<std::is_const<Cont>::value,
typename Cont::const_pointer,
typename Cont::pointer>::type;
Cont& data_;
const static size_t offset_ = 1;
Pointer data_view;
public:
ContainerHolderSecond(Cont& data) : data_(data), data_view(&data[offset_]) {}
};
template <typename Cont>
class ContainerHolderBarry {
using Pointer = decltype(&std::declval<Cont&>()[0]);
Cont& data_;
const static size_t offset_ = 1;
Pointer data_view;
public:
ContainerHolderBarry(Cont& data) : data_(data), data_view(&data[offset_]) {}
};
int main() {
using namespace std;
array<int, 2> my_array;
ContainerHolderFirst<array<int, 2>> holder(my_array); // works
const array<int, 2> const_array{5,7};
// ContainerHolderFirst<const array<int, 2>> const_holder(const_array);
/* error: invalid conversion from 'const value_type* {aka const int*}' to 'std::array<int, 2ull>::pointer {aka int*}' [-fpermissive] */
ContainerHolderSecond<array<int,2>> second_holder(my_array); // works!
ContainerHolderSecond<const array<int,2>> const_holder(const_array); //updated; works as well; awkward
ContainerHolderThird<array<int,2>> third_holder(my_array); // still works
ContainerHolderThird<const array<int,2>> third_const_holder(const_array); //finally compiles as well
ContainerHolderBarry<array<int,2>> barry_holder(my_array);
ContainerHolderBarry<const array<int,2>> barry_const_holder(const_array);
}
You're making this unnecessarily difficult on yourself. If you want the type of &cont[offset], just ask for the type of that expression. Use std::declval along with decltype:
template <typename Cont>
class ContainerHolder {
using Pointer = decltype(&std::declval<Cont&>()[0]);
...
};
The only problem with ContainerHolderSecond is that you're using it incorrectly:
ContainerHolderSecond<array<int,2>> const_holder(const_array);
// ^-- insert "const" here
As for ContainerHolderFirst, the reason that array<T, N>::pointer is the same type as (array<T, N> const)::pointer is that there is no automatic way to determine where the const qualification should be added to the nested type, and there is no language facility to describe this (that is, we don't have const-qualified typedefs or type aliases).
now i have
template<class C> class Array
{
inline int Search(const C &value) const;
...
}
i want to use in this way
Array<int *> a;
int i;
const int *pi = &i;
a.Search(pi);
but i got
error C2664: 'A::Search' : cannot convert parameter 1 from 'const
int *' to 'int *const &'
is it possible to workaround it somehow?
Only i can think for now it is to partially specialize this claxx for pointers, but i'm not sure.
template<class C> class Array<C*>
{
inline int Search(const C *&value) const;
...
}
Is it good way, maybe it is possible to do it without creating partial specialization?
As you have noticed, your problem comes from the fact that you're adding a top-level const to your template parameter, but when it comes to pointers it yields T * const not T const *.
Specializing your template is one way to do achieve what you want.
Another way would be to make a helper template that takes care of the deep "constification" and use this in your main template. This is usually easier to do it that way because it means less code duplication. Something along the lines of:
template<typename C>
struct Constify {
typedef const C type;
};
template<typename C>
struct Constify<C*> {
typedef const C* const type;
};
template<class C>
class Array
{
inline int Search(typename Constify<C>::type & value) const;
...
};
I'm trying to use Boost::Fusion to transform a list of function's parameter types into a fusion::list. Ultimately, I am trying to turn a list of variables into parameters that I can call a function with (http://stackoverflow.com/questions/11164914/generating-wrappings-for-c-functions).
I've gotten this to work for non-referenced variables. However, it fails to compile for non-referenced variables when I try to turn the function's parameter list (specifically on the fusion::to_list it complains it can't deref the iterator).
I've simplified the code down by a bit below:
struct identity {
template<typename Sig> struct result;
template <typename T>
struct result<convert(T)> { typedef T type; };
template <typename T>
typename T operator ()(T) const {
return T();
}
};
int main(int argc, char **argv) {
typedef BOOST_TYPEOF(foo) params_type;
auto seq = function_types::parameter_types<params_type>();
auto transformed = fusion::transform(seq, identity());
auto passedParams = fusion::as_list(transformed);
}
If foo is defined as:
int foo(int a) { return 5*a; }
it works fine, but it breaks on:
int foo(int &a) { return 5*a; }
For the purposes of my code, I don't actually need the references kept in the sequence, which I am assuming is the issue (also, searches I've done tend to point that as being the culprit). However, I'm not completely sure of how to strip the transformed function of these references before as_list is called.
I tried something along the lines of:
template <typename T>
struct result<convert(T)>: remove_reference<T> {};
template <typename T>
typename remove_reference<T>::type operator ()(remove_reference<T>::type) const { return typename remove_reference<T>::type(); }
but got the same compile errors.
Any ideas of how to fix this?
update
Here is the truncated compiler error I get (with clang++ --std=c++0x) for both cases given above:
/usr/local/include/boost/fusion/adapted/mpl/mpl_iterator.hpp:43:24: error:
reference to type 'int' requires an initializer
return type();
^
/usr/local/include/boost/fusion/iterator/deref.hpp:61:28: note: in instantiation
of member function
'boost::fusion::mpl_iterator<boost::mpl::v_iter<boost::function_types::parameter_types<void
(int &), boost::add_reference<mpl_::arg<-1> > >, 0>
>::deref<boost::fusion::mpl_iterator<boost::mpl::v_iter<boost::function_types::parameter_types<void
(int &), boost::add_reference<mpl_::arg<-1> > >, 0> > >::call' requested
here
return deref_meta::call(i);
...
test4.cpp:65:22: note: in instantiation of function template specialization
'boost::fusion::as_list<boost::fusion::transform_view<const
boost::function_types::parameter_types<void (int &),
boost::add_reference<mpl_::arg<-1> > >, convert, boost::fusion::void_> >'
requested here
auto passedParams = fusion::as_list(transformed);
If your compiler is C++11 compatible, you might want to look into the `std::remove_reference function. Or at least try to find an implementation of it and use as reference for making your own.
I am not sure if title makes sense.
let me explain what I want to do:
I have template constructor, the argument generally must be reference, but can be value in certain cases.
Ultimately, I would like to have something like:
matrix_adapter(typename adaptable<T,A>::type);
where adaptable<T,A>::type can either be value or reference defending on type of A.
I am not able to do so (template instantiation does not happen), and unsure why.
Right now, my solution is to enable/disable constructor using Sfinae:
matrix_adapter(A a, typename adaptable<T,A>::pass_by_value = 0)
however, that requires two constructors which are essentially the same.
Is it possible to do it using one generic constructor?
more code:
template<typename T>
struct adaptable<T, boost::detail::multi_array::const_sub_array<T, 2, const T*> >
{
typedef void* pass_by_value;
typedef boost::detail::multi_array::const_sub_array<T, 2, const T*> type;
static size_t size1(const type &A) { return A.shape()[0]; }
static size_t size2(const type &A) { return A.shape()[1]; }
static const T* data(const type &A) { return A.origin(); }
};
template<typename T, class L = ublas::column_major>
struct matrix_adapter
: ublas::matrix<T, L, ublas::array_adaptor<T> >
{
typedef ublas::array_adaptor<T> array_type;
typedef ublas::matrix<T, L, array_type> base;
matrix_adapter(size_t size1, size_t size2, T *data)
: base(size1, size2, array_type(size1*size2, pointer()))
{
initialize(data);
}
template<class A>
matrix_adapter(A a, typename adaptable<T,A>::pass_by_value = 0)
: base(adaptable<T,A>::size1(a),
adaptable<T,A>::size2(a),
array_type(adaptable<T,A>::size1(a)*
adaptable<T,A>::size2(a),
pointer()))
{
initialize(adaptable<T,A>::data(a));
}
template<class A>
matrix_adapter(A &a)
: base(adaptable<T,A>::size1(a),
adaptable<T,A>::size2(a),
array_type(adaptable<T,A>::size1(a)*
adaptable<T,A>::size2(a),
pointer()))
{
initialize(adaptable<T,A>::data(a));
}
Your constructor is itself a template, but with a signature of matrix_adapter(typename adaptable<T,A>::type) the compiler cannot deduce the type of A from the call.
By using SFINAE you enable the compiler to deduce A from the first constructor argument, and then the second argument prevents one or other from being considered in the overload set.
I don't believe it is possible to eliminate either constructor.