Template for a family of functions designed to copy/convert objects - c++

I would like to write a family of copy/conversion functions named copy that copies from object A to object B. For instance
void f(const A& a) {
B b = copy<B>(a);
...
}
For instance A could be a std::array<double, 5> and B could be a std::vector<double>.
Is it possible to do such a thing and define for once the specialization that converts from any std::array<T, n> to std::vector<T>? In another file I would also like to define for once the specialization that converts from any std::array<T, n> to CudaVector<T>. In another file I would like to define the conversion from SparseMatrix<T> to DenseMatrix<T>, etc...
If you don't find any solution for that, do you think of any way to do something similar, as long as I still have value semantics (I don't want to define copy(const A& input, B& output)).

This is certainly possible thanks to template argument deduction. The from-type can be deduced from the argument, the return type needs to be explicitly stated.
There's one other problem and that is that if the plan is to have specializations for converting various containers to various other containers, you would need partial function specializations, which are not allowed. But an usual trick can work around that using partial specializations of classes instead.
For example, this works as a generic template and its specialization for copy-converting a std::array to a std::vector of the same element type:
template<typename B, typename A>
struct copier {
//static B copy(const A&); // Is there a generic fall-back algorithm? Probably not.
};
template<typename T, size_t N>
struct copier<std::vector<int>, std::array<T, N>> {
static std::vector<int> copy(const std::array<T, N>& a) {
std::vector<int> b{};
std::copy(a.begin(), a.end(), std::back_inserter(b));
return b;
}
};
template<typename B, typename A>
B mcopy(const A& a) {
return copier<B, A>::copy(a);
}
Then you can call this using
std::array<int, 3> a{1, 2, 3};
using B = std::vector<int>;
B b = mcopy<B>(a);
which looks just like the line from your question.
Note that copy is not a very good name for this function. ADL would kick in and find std::copy as long as the type A is in namespace std, which might lead to very strange error messages.

Related

std::array derived class aggregate initialization

I am making a small helper class that derives from std::array. The constructor does not inherit, obviously, and it is that which is responsible for brace-initialization; for example:
template<typename T, size_t size>
struct foo : std::array<T,size>
{
foo(int a, int b)
: std::array<T,size>{a,b}
{
//nothing goes here since constructor is just a dummy that
//forwards all arguments to std::array constructor
}
}
int main()
{
foo<int,2> myobj = {1,2}; //brace initialization calls custom constructor with inner elements as arguments
}
The amount of arguments has to match exactly, so I am leaning towards using something like a variadic function argument in the constructor (since I am not only going to be using 2 elements in the array every single time). Using this, how would I forward the variadic argument pack to the std::array constructor? I am open to other methods of brace initialization that allow forwarding to the std::array constructor.
Note: std::initializer_list requires runtime initialization, and i am looking for a compile time/constexpr compatible method. Thank you.
You can use a perfect-forwarding constructor:
template<class... U>
foo(U&&... u)
: std::array<T, size>{std::forward<U>(u)...}
{}
I don't think that inheriting from a standard container is a good idea.
Anyway...
You can use variadic templates, perfect forwarding and also SFINAE to impose that the number of arguments is exactly size.
You can also make constexpr the foo constructor so you can make constexpr foo objects.
By example
#include <array>
#include <type_traits>
template <typename T, std::size_t S>
struct foo : public std::array<T, S>
{
template <typename ... As,
typename std::enable_if<sizeof...(As) == S>::type * = nullptr>
constexpr foo (As && ... as)
: std::array<T, S>{ { std::forward<As>(as)... } }
{ }
};
int main ()
{
//constexpr foo<int, 2u> myobj1 = {1}; // compilation error
constexpr foo<int, 2u> myobj2 = {1, 2}; // compile
//constexpr foo<int, 2u> myobj3 = {1, 2, 3}; // compilation error
}

Initialization of std::array<T,N> in constructor initializer list when T is not default-constructible

Motivated by this question, I wonder whether it's safe to construct std::array<T,N> in a constructor initializer list, if both T and N are template parameters and T is not default-constructible. I came up with the following helper functions:
template <typename T, size_t... I>
constexpr auto make_array_impl(std::index_sequence<I...>, const T& val) {
return std::array<T, sizeof...(I)>{ (I, val)... };
}
template <typename T, size_t N>
constexpr auto make_array(const T& val) {
return make_array_impl<T>(std::make_index_sequence<N>{}, val);
}
Now, if I have a class such as:
class E {
public:
E() = delete;
E(int, int, int) { }
};
I can write:
template <typename T, size_t N>
class A {
public:
template <typename... Args>
A(Args&&... args) : a_(make_array<T, N>(T(std::forward<Args>(args)...))) { }
private:
std::array<T, N> a_;
};
And then
A<E, 5> a(1, 2, 3);
This works for me with GCC 8.1 / Clang 6.0 and C++14 enabled. However, I am not sure whether such initialization of a_ in the constructor initializer list is correct, and if so, whether this corectness depends on template parameter T.
(To say the truth, I even can't find in the Standard whether std::array can be initialized by another std::array. Is it a part of aggregate-initialization? Or, does std::array support copy-initialization?)
To clarify the question, what I want to achieve by
A<E, 5> a(1, 2, 3);
is that a::a_ member function would effectively be initiated as
std::array<E, 5> a::a_ = { E(1,2,3), E(1,2,3), E(1,2,3), E(1,2,3), E(1,2,3) };
Unless you delete the copy constructor (or the implicit one is deleted, which requires that some subobject has a deleted copy constructor or something of that nature), every type can undergo copy construction. So for a value t of any (non-array object) type T, if it is copy constructible, T t2 = t; will work.

Generic functions taking generic functions as arguments

I can't find a nice way to define generic higher-order functions taking generic functions as arguments. For example, take this attempt at one of the simplest such functions out there:
template<typename F, typename A>
auto apply(F f, const A& a) -> decltype(f(a)){return f(a);}
Of course it works as intended when used with non-template functions. But if I have, for instance
template<typename A>
A id(const A& a){return a;}
then
int a = 10;
int b = apply(id, a);
will not work, since id expects a template parameter. I could just write id<int> to make it work, but that sort of defeats the purpose (as I see it, it implies that if I wanted to write "filter" I'd have to write a separate definition for each generic predicate function). Using std::function or function pointers did not help. Also, I tried to make a "template template" version of apply, but I get various compiler errors when I try to use it:
template<template<typename> class F, typename A>
auto apply2(F<A> f, const A& a)-> decltype(f(a)){return f(a);}
The best I came up with was the following:
struct Id{
template<typename A>
static A func(const A& a){return a;}
};
template<typename F, typename A>
auto apply(A a)-> decltype(F::func(a)){return F::func(a);}
It's a bit ugly, but now at least I can actually parameterize by the function.
So, is there a better way to do generic functions taking generic functions as arguments?
This 'functor' works with your first version of apply.
struct id
{
template<typename A>
A operator ()(const A& a) {return a;}
};
later
int b = apply(id(), a); // with extra parenthesis to construct the struct.
In C++11 the best you can do is to use a struct with a templatized function call operator:
struct ID {
template <typename T>
void operator()(T arg) const {
...
}
};
The reason this is preferrable to a function [pointer] because it more likely to be inlined. With C++14 you could use lambdas:
[](auto arg){ ... }
which is just a cute version to write the struct above.

C++ methods which take templated classes as argument

I have a templated class
Vector<class T, int N>
Where T is the type of the components (double for example) and n the number of components (so N=3 for a 3D vector)
Now I want to write a method like
double findStepsize(Vector<double,2> v)
{..}
I want to do this also for three and higher dimensional vectors. Of course I could just introduce further methods for higher dimensions, but the methods would have a lot of redundant code, so I want a more generic solution. Is there a way to create a method which takes a templated class without further specializing it (in this case without specifying T or N)? Like
double findStepsize(Vector<T,N> v)
?
Yes it is
template<typename T, int N>
double findStepsize(Vector<T,N> v)
{..}
If you call it with a specific Vector<T, N>, the compiler will deduce T and N to the appropriate values.
Vector<int, 2> v;
// ... fill ...
findStepsize(v); /* works */
The above value-parameter matches your example, but it's better to pass user defined classes that need to do work in their copy constructors by const reference (Vector<T, N> const& instead). So you avoid copies, but still can't change the caller's argument.
Implement it this way:
template <typename A, int B>
class Vector {
};
template <typename T, int N>
void foo(Vector<T, N>& v) {
}
template <>
void foo(Vector<int, 3>& v) {
// your specialization
}
template <typename T, size_t N>
T find_step_size( const Vector<T,N>& v )
{
return T(); // or something
}
Your second question answer:
You can't have a templated pointer to function, that makes no sense.
But what you can do is
#include <vector>
template <typename T>
void foo(const std::vector<T>& v) {
// do something
}
void (*ptr_foo)(const std::vector<int>&) = &foo<int>;
(here the function pointers a templated function, which template argument is explicitly set to int)

Passing std::vector for any type to a function

Given:
template<typename T> class A {
B b;
std::vector<T> vec1;
std::vector<T> vec2;
}
I'd like B to have a member function that fill() that takes a reference to those to vectors and fills vec2 with values of T depending on some information contained in b.
One way of doing this is overloading fill() for each possible argument T:
fill(const std::vector<float>& a, std::vector<float>& b)
and so on but this will mean a lot of unnecessary duplication as the operations are the same for every possible T. Inside of fill() I could use vector::value_type for the calculations but I don't know how to declare it in such a way that it takes every kind of std::vector. The obvious way would be to use a free function with templates. Is there a simpler way of doing this?
Templatize B.
template<typename T> class B {
void fill(const std::vector<T>& a, std::vector<T>& b) { }
};
template<typename T> class A {
B<T> b;
std::vector<T> vec1;
std::vector<T> vec2;
}
If you don't want to templatize B, then templatize the fill function:
class B {
template<typename T>
void fill(const std::vector<T>& a, std::vector<T>& b) {}
};
You've gotten a number of answers, but I have to disagree with them, to at least some degree. My immediate reaction is that you shouldn't pass a vector to b::fill at all. Rather, you should pass an iterator (or maybe a pair of iterators). The rest is mostly right though: that still means fill should be a template member function. When you call it, you'll probably want to pass a std::back_insert_iterator, usually obtained with std::back_inserter.
Part of what you've said seems self-contradictory though: if b::fill modifies vec1 and vec2, they probably should not be passed as references to const. Admittedly, const doesn't have exactly its usual meaning when applied to a container, but the fact remains that passing a reference to const to a function whose sole intent is apparently to modify what's passed seems wrong.
Templatize fill:
class B {
public:
template<typename T>
void fill(const std::vector<T>& a, std::vector<T>& b)
{ /*...*/ }
//...
};
(From your description it sees that b should be a const std::vector<T>&.)
You can define B as template class, fill as template function (inside a non-template class B) or, my favorite, use the standard std::transform/std::copy/std::fill, which are already template function, to populate your vector.
(All located inside <algorithm> header).