Parameter pack constructor hiding user defined conversion in class template - c++

I am writing a simple class template for mathematical vectors. I have within the template a constructor template using a parameter pack so that the vector can be constructed with a variable number of parameters:
template <unsigned int N, typename T>
class Vec
{
public:
T m_components[N];
Vec()
: m_components{}
{ }
template<typename ...Args>
explicit Vec(Args... args)
: m_components{args...}
{ }
// ...
}
I have also added user defined conversion to convert between classes using this template:
// ...
template <unsigned int _N, typename _T>
explicit operator Vec<_N, _T> () const
{
Vec<_N, _T> vec;
for(unsigned int i = 0; i < N && i < _N; ++i)
vec.m_components[i] = static_cast<_T>(m_components[i]);
return vec;
}
// ...
However when I call on the user-define conversion I have written like so:
Vec<2, int> a(2, 8);
Vec<4, double> b;
b = (Vec<4, double>)a;
The line where the C-style cast is being used causes the following error:
C2440: 'initializing': cannot convert from 'Vec<2, int>' to 'T' // points to the expansion of the parameter pack: m_components{args...}
I can see when I step through the code that the C-style cast goes straight to the parameter pack constructor. Why is this is and what am I doing wrong? How can I prevent the parameter pack constructor from seemingly hiding the user defined conversion?

Add this constructor:
// ...
template <unsigned int _N, typename _T>
explicit Vec(const Vec<_N, _T>& other)
{
for(unsigned int i = 0; i < N && i < _N; ++i)
m_components[i] = static_cast<_T>(other.m_components[i]);
}
// ...
As I understand, and I might be very wrong here (someone with more expertise could explain better), the compiler does not know how to modify the current instance of your template class in order to be assigned with the other one.
The m_components was allocated with a fixed size during construction. Even with the overload cast operator you only did half the job. There has to be some mechanism for the current instance to be modified in such a way that accepts the data from another one.

Related

How to disable a member function based on a class template parameter?

To illustrate the situation, let's assume a minimal example: a Vector template class taking its dimension as a non-type template parameter. This class will provide x(), y() (etc.) accessors when the dimension allows it:
template <int N_dimension>
class Vector
{
public:
// ctors, etc.
int &x();
template <class = std::enable_if_t<(N_dimension>2)>> int &y();
private:
std::array<int, N_dimension> mData;
};
Yet, this does not work, because enable_if can only be applied on deduced template parameters.
Our current workaround looks cumbersome:
template <int N=N_dimension, class = std::enable_if_t<(N>2)>> int &y();
Moreover, it also requires a static-assert in the definition to make sure it is fool-proof (because now client code could give an explicit value to N that does not match the real dimension. Edit: Or an explicit value for the anonymous second template parameter, as pointed out by SergeyA).
Is there a more direct approach to express this in C++?
I would do away with SFINAE here, and simply split the code into interface and private implementation like that:
int& y() {
return y_impl(std::bool_constant<N > 2>{});
}
private:
int& y_impl(std::true_type ) {
// impl
}
int& y_impl(std::false_type ) {
static_assert(N > 2 /* always false */, "Wrong number of dimensions!");
}
The split here is in assumption of y not compilable when N <= 2, to reduce the clutter of error messages. If this is not the case, a single static_assert in y body would be sufficient.
In C++20, you might simply use requires to discard method:
template <int N>
class Vector
{
public:
int &x();
int &y() requires(N >= 2);
private:
std::array<int, N_dimension> mData;
};
In previous version, it is more verbose:
template <std::size_t N>
class Vector
{
public:
int &x();
template <std::size_t M = N, std::enable_if_t<(M >= 2 && M == N), int> = 0>
int &y();
private:
std::array<int, N_dimension> mData;
};

Eigen Allocator causing Trouble in Templated Class

I have this class:
typedef vector<Eigen::Affine3d,Eigen::aligned_allocator<Eigen::Affine3d> > VoteList;
template <class T>
class KernelDensityEstimator {
public:
KernelDensityEstimator() {}
void setData(vector<T>& d) {
data = d;
}
void setKernel(double (*k)(T&, T&)) {
kernel = k;
}
double evaluate(T& p) {
double sum;
for (int i = 0; i < data.size(); i++) {
sum += (*kernel)(p, data[i]);
}
return sum/data.size();
}
private:
double (*kernel) (T&, T&);
vector<T> data;
};
I want to use with with the type T = Eigen::Affine3d for now. However, when I call setData() it's causing me troubles because Eigen requires to specify an Eigen::aligend_allocator for std containers to use with Eigen.
So when I give a vector<Eigen::Affine3d,Eigen::aligned_allocator<Eigen::Affine3d> > (aka VoteList) as the input parameter to setData() my compiler complaines that:
no known conversion for argument 1 from ‘VoteList {aka std::vector<Eigen::Transform<double, 3, 2>, Eigen::aligned_allocator<Eigen::Transform<double, 3, 2> > >}’ to ‘std::vector<Eigen::Transform<double, 3, 2>, std::allocator<Eigen::Transform<double, 3, 2> > >&’
Which kind of makes sense, but I thought that the allocator is part of the object type. Is there a way around this and keeping my KernelDensityEstimator generic?
You can enable Eigen's std::vector specializations for the types you are planing to use, as detailed there, e.g.:
#include<Eigen/StdVector>
EIGEN_DEFINE_STL_VECTOR_SPECIALIZATION(Eigen::Affine3d)
EIGEN_DEFINE_STL_VECTOR_SPECIALIZATION(Eigen::Matrix4d)
...
You basically want to have a different type for your member data depending on the class template parameter type T. There are several ways to do that, here is one suggestion:
Define a special storage type for your class, in which you keep the data (best put it in a suitable namespace or inside your class):
//normal storage type
template<typename T>
struct storage_type_impl
{
using type = std::vector<T>;
};
//storage type for Eigen::Transform types
template<typename ... Args>
struct storage_type_impl<Eigen::Transform<Args ...> >
{
using type = std::vector<Eigen::Transform<Args ...>
, Eigen::aligned_allocator<Eigen::Transform<Args ...> > >;
};
template<typename T>
using storage_type = typename storage_type_impl<T>::type;
Now you can use that inside your class, together with a templated setData method (that is sufficient as the compiler will still complain if it cannot perform the data copy/move):
template <class T>
struct KernelDensityEstimator
{
template<typename D>
auto setData(D&& d)
{
data = std::forward<D>(d); //calls move assignment if d is an rvalue
//otherwise a copy is performed
}
//...
private:
storage_type<T> data;
//...
};
The code is untested as I don't have Eigen available at the moment, but I hope it's still able to explain the basic idea.

Compile time error if brace-closed list is the wrong size for class constructor

I'm trying to write a class based around mathematical vectors:
template <unsigned N> class Vector{
public:
Vector() = default;
Vector(std::initializer_list<double> li) { *this = li;}
Vector& operator=(std::initializer_list<double>);
private:
std::array<double, N> x = {}
}
template <unsigned N> inline Vector<N>& Vector<N>::operator=(std::initializer_list<double> li){
if(N != li.size()) throw std::length_error("Attempt to initialise Vector with an initializer_list of different size.");
std::copy(li.begin(), li.end(), x.begin());
return *this;
}
I want to be able to write code like this;
Vector<3> a = {1,2,3};
a = {3,5,1};
It would be natural for a user to expect to write code like that, right? However I want compile-time errors to occur if I use the wrong-sized initializer list, much like std::array does.
std::array<double, 3> a = {2,4,2,4} //compile time error
Vector<3> a = {3,5,1,5} //run-time error as of right now
My first idea was to use std::array as the constructor/operator parameter so implicit conversions would occur and then the constructor would hijack, from std::array, the compile time errors. Except of course I could only write code like this:
Vector<3> a({2,3,2}); //fine
Vector<3> b = {2,4,2}; //error, requires two user-defined conversions (list -> array<double,3> -> Vector<3>)
I thought maybe to use a Variadic member template:
template <typename... Args> Vector(Args... li): x({li...}){
static_assert(sizeof...(li) == N);
}
It has to be typename... rather than double... because nontype parameters must be integral types. But then I run in to a narrowing conversion error
Vector<2> a = {3,2} //error: narrowing conversion of 'li#0' from 'int' to 'double' inside { } [-Wnarrowing]|
//error: narrowing conversion of 'li#1' from 'int' to 'double' inside { } [-Wnarrowing]|
Presumably for violating [8.5.4]/7
A narrowing conversion is an implicit conversion
— from an integer type or unscoped enumeration type to a floating-point type, except where the source is a constant expression and the actual value after conversion will fit into the target type and will produce the original value when converted back to the original type, or
The parameters from expanding li... aren't constant expressions and hence produce the narrowing conversion error. As far as I'm aware it wouldn't even be possible to make function parameters as constant expressions (nor would it make much sense?). So I'm not sure how to carry on down that route. Obviously Vector<2> a = {2.,3.} works fine but this puts a burden on the user to remember only to supply floating-point literals.
You can make your constructor a variadic template so that any condition can be used:
#include <array>
#include <cstddef>
template<typename T, std::size_t N>
class vector
{
public:
vector(T&& value)
: data{static_cast<T>(value)}
{}
template<typename U>
vector(const vector<U,N>& v)
{
std::copy(begin(v.data), end(v.data),
begin(data));
}
template<typename U>
vector(const vector<U,N>& v)
{
std::copy(begin(v.data), end(v.data),
begin(data));
}
template<typename... U,
typename = typename std::enable_if<sizeof...(U)-1>::type>
vector(U&&... values)
: data{static_cast<T>(values)...}
{
static_assert(sizeof...(values) == N, "wrong size");
}
std::array<T,N> data;
};
int main()
{
vector<int, 3> v = {1,2,3};
vector<double, 4> vv = {5,4,3,2};
vv = {1,2,3,4};
//vector<float, 3> vf = {1,2,3,4}; // fails to compile
vector<float,3> vf = v;
}
Live example on coliru.
It gets you a custom error message, easily adaptable/extensible condition for failure, and gets rid of the "narrowing conversion" problem by effectively forwarding the initialization to the std::array initializer like you wanted to do in the first place. Oh, and you get assignment for free.
As #M.M mentions, this solution ruins copy construction, unfortunately. You can solve it by adding an enable_if on the variadic arguments "array" size as shown above. Of course you need to be careful to not ruin assignment/copy-construction and single-element vectors, which is remedied by adding two extra constructors for these special cases.
This code seems to work, for both constructor and assignment-operator:
#include <array>
template<size_t N>
struct Vector
{
Vector() = default;
template<typename...Args>
Vector(double d, Args... args)
{
static_assert(sizeof...(args) == N-1, "wrong args count");
size_t idx = 0;
auto vhelp = [&](double d) { x[idx++] = d; };
vhelp(d);
double tmp[] { (vhelp(args), 1.0)... };
}
Vector &operator=(Vector const &other) = default;
private:
std::array<double, N> x = {};
};
int main()
{
Vector<5> v = { 1,2,3,4,5 };
v = { 3,4,5,6,7 };
Vector<1> w = { 1,2 }; // error
}
The assignment operator works because the constructor is implicit, so v = bla attempts to convert bla to match the only definition of operator=.
I made the first argument double d instead of just using all variadic args, to avoid the issue where all-variadic-args constructor catches calls that were supposed to be copy-construction.
The line involving double tmp[] uses what I call the variadic template comma operator hack. This hack has many uses, but here it lets us avoid the narrowing-conversion issue that double tmp[] { args... }; has.
(TBH though, incorporating rubvenvb's idea and using double tmp[] { static_cast<double>(args)... }; would be simpler)

Implicit conversion to template

My example below suggests that implicit conversions from non-template types to template types won't work as seamlessly as those only involving non-template types. Is there a way to make them work nonetheless?
Example:
struct point;
template<unsigned d> struct vec {
vec() { }
// ...
};
template<> struct vec<2> {
vec() { }
vec(const point& p) { /* ... */ } // Conversion constructor
// ...
};
struct point {
operator vec<2>() { return vec<2>(/* ... */); } // Conversion operator
};
template<unsigned d> vec<d> foo(vec<d> a, vec<d> b) {
return vec<d>(/* ... */);
}
template<unsigned d1, unsigned d2>
vec<d1 + d2> bar(vec<d1> a, vec<d2> b) {
return vec<d1 + d2>(/* ... */);
}
int main(int argc, char** argv) {
point p1, p2;
vec<2> v2;
vec<3> v3;
foo(v2, p1);
foo(p2, v2);
foo(p1, p2);
bar(v3, p1);
}
Is there a way to let this code auto-convert from point to vec<2>?
I know I can overload foo and bar to allow for point arguments, delegating to the vec implementation using an explicit conversion. But doing this for all parameter combinations will become tedious, particularly for functions with many such parameters. So I'm not interested in solutions where I have to duplicate code for every parameter combination of every function.
It appears that neither the conversion constructor nor the cast operator are sufficient to achieve this. At least my gcc 4.7.1 reports no matching function call, although it does name the desired function in a notice, stating that ‘point’ is not derived from ‘vec<d>’.
There is no direct way to get the conversion from point to vec<2>, because at the time when the function call foo(v1,p1) is processed, a function foo that expects a vec<2> as second argument does not exist yet. It's just a function template, and in order for this to be instantiated to a foo(const vec<2> &,const vec<2> &), a function call with these exact argument types would have to be given.
In order for the code to work, the compiler would have to guess both how to instantiate the template parameters, and what type the point argument to convert to. This is too much in the general case (although in your particular code it appears simple, because there is no other possible way to interpret the intent of the programmer).
In terms of solving this, the only thing I can think of is to create highly templated conversion functions:
template <typename T>
struct make_vec
{ };
template <unsigned d>
struct make_vec<vec<d>>
{
static constexpr unsigned dim = d;
using type = vec<dim>;
static const type &from(const type &v)
{ return v; }
};
template <>
struct make_vec<point>
{
static constexpr unsigned dim = 2;
using type = vec<dim>;
static type from(const point &p)
{ return type(p); }
};
template <typename T>
typename make_vec<typename std::decay<T>::type>::type make_vec_from(T&& arg)
{ return make_vec<typename std::decay<T>::type>::from(std::forward<T>(arg)); }
And then implement the foo and bar functions as general templates (accepting all kinds of types, not only vec<d>, using make_vec defined above to convert the given types to the right kind of vec<d>):
namespace detail {
/* Your original implementation of foo. */
template<unsigned d> vec<d> foo(vec<d>, vec<d>) {
return vec<d>(/* ... */);
}
}
/* Templated version of foo that calls the conversion functions (which do
nothing if the argument is already a vec<d>), and then calls the
foo() function defined above. */
template <typename T, typename... Ts>
typename make_vec<typename std::decay<T>::type>::type foo(T&& arg, Ts&&... args)
{ return detail::foo(make_vec_from(arg),make_vec_from(args)...); }
In the case of bar you also need a way to calculate the return type, which is vec<d1+d2+d3...>. For this, a sum calculator is required, also templated:
template <typename... Ts>
struct dsum {
static constexpr unsigned value = 0;
};
template <typename T, typename... Ts>
struct dsum<T,Ts...> {
static constexpr unsigned value = make_vec<typename std::decay<T>::type>::dim + dsum<Ts...>::value;
};
Then, the return type of bar() is vec<dsum<T,Ts...>::value>.
A fully working example is here: http://liveworkspace.org/code/nZJYu$11
Not exactly simple, but might be worth it if you really have extremely many different combinations of arguments.

C++ template argument reference/value trait

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.