I am trying to initialize an array inside a template class and pass the this pointer to all elements in the array.
This is what my classes might look like:
template<int NUM> class outer_class;
template<int N>
class inner_class {
private:
outer_class<N> *cl;
public:
inner_class(outer_class<N> *num) {
cl = num;
}
void print_num() {
cl->print_num();
}
};
template<int NUM> class outer_class {
private:
int number = NUM;
// --> here I basically want NUM times 'this' <--
std::array<inner_class<NUM>, NUM> cl = { this, this, this, this };
public:
void print_num() {
std::cout << number << std::endl;
}
void print() {
cl[NUM - 1].print_num();
}
};
int main() {
outer_class<4> t;
t.print();
return 0;
}
How can I pass the this pointer to all elements of inner_class stored in the array of outer_class (in C++11)?
First, you can't have this like this outside of constructor or any other member function. Here you have to initialize cl within initializer list.
Using delegating constructor together with std::*_sequence stuff:
template<int NUM> class outer_class {
...
template <std::size_t... Integers>
outer_class(std::index_sequence<Integers...>)
: cl{(static_cast<void>(Integers), this)...}
{}
public:
outer_class(/* whatever */) : outer_class(std::make_index_sequence<NUM>{}) {}
};
Side notes:
Your print member functions should be marked const as they don't modify your members.
cl[NUM - 1].print_num(); you might want to use std::array::back().
You could use some helper functions and then initialize the member using these functions, e.g.:
template <std::size_t I, class T>
T copy(T t) { return t; }
template <class T, std::size_t... Is>
constexpr std::array<T, sizeof...(Is)> copy_n(T const& t, std::index_sequence<Is...>) {
return {copy<Is>(t)... };
}
template <class T, std::size_t N>
constexpr std::array<T, N> copy_n(T const& t) {
return copy_n(t, std::make_index_sequence<N>{});
}
Then in your class:
std::array<inner_class<NUM>, NUM> cl;
outer_class() : cl(copy_n<inner_class<NUM>, NUM>(this)) { }
Note:
[to be verified] you cannot use this in default member initializer, so you need to have a custom constructor;
you need to explicitly specify inner_class<NUM> as the first template parameter for copy_n, because otherwize T will be deduced as outer_class<NUM>*, and while there is an implicit conversion from outer_class<NUM>* to inner_class<NUM>, there is no conversion from std::array<outer_class<NUM*>, NUM> to std::array<inner_class<NUM>, NUM>;
if you are using C++11 and not 14, or clang, you might get a warning on the return of copy_n, you can get rid of it by adding an extra pair of brackets {}.
Related
I would like to define a class template (hereafter called C) which takes a reference to an object of an to-be instantiated class template (hereafter called S) as template parameter. The objective is that C can be fully instantiated with one template argument.
S is a class template on its own which has one integral type template parameter. The C class template shall be instantiated using a reference to an object of any instantiation of S.
This is what I am trying to achieve:
template<int I> struct S {
int get() { return 42 + I; }
};
// ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ my desperate attempt
template< typename S<int I>::template & n>
struct C {
int get() {
return n.get();
}
};
S<42> s;
int main()
{
C<s> c;
return c.get();
}
The compiler I am using supports GNU++11 or older.
In C++17, you might do
template<int I> struct S { int get() { return 42 + I; } };
template <auto& n>
struct C;
template <int I, S<I>& n>
struct C<n>
{
int get() { return n.get(); }
};
S<42> s;
int main()
{
C<s> c;
return c.get();
}
Demo.
Before C++17, template <auto& n> struct C; has to be replaced. for example by
template <typename T, T& n> struct C;
template <typename T, T& n>
struct C;
template <int I, S<I>& n>
struct C<S<I>, n>
{
int get() { return n.get(); }
};
S<42> s;
#define AUTO(x) decltype(x), x
int main()
{
C<S<42>, s> c;
// C<AUTO(s)> c;
return c.get();
}
Demo
There is no way that I know in C++11 that will allow you to change just the template parameters to do what you want. What you can do though is not have a non-type template parameter, but just a type, and add a constructor to C that takes the reference to the desired object as a parameter:
template<typename T>
struct C {
C(T &t): t(t) {}
int get() {
return t.get();
}
private:
T &t;
};
Then you could declare c as follows:
C<decltype(s)> c(s);
However, it is of course not so nice to have to repeat yourself like that, so the trick is to make a templated function that will construct a C of the right type for you:
template<typename T>
C<T> make_C(T &t) {
return C<T>(t);
}
And then you can write:
auto c = make_C(s);
This is not the answer. But maybe this helps somebody who stumbled upon this question or even help somebody to actually find the answer.
In contrast to the original question I added the static member variable S.i.
template<int I> struct S {
static constexpr int i = I;
int get() { return 42 + I; }
};
template<int I, S<I>& n> struct C
{
int get() {
return n.get();
}
};
S<42> s;
int main()
{
C<s.i, s> c;
return c.get();
}
This is not the answer, because still two template arguments are required in order to instantiate the class template C.
This compiles with C++11.
I have a class that contains an array of objects T without a default constructor. Here is an example:
#include <iostream>
struct Param {
int x;
};
struct A {
A(const Param &p) {
std::cout << p.x << std::endl;
}
};
template<class T, int n>
struct Array {
T data[n];
/* n times */
Array(const Param &p) : data{/* T(p), ..., T(p) */} { }
};
int main() {
Param p{42};
Array<A, 3> arr(p);
return 0;
}
I am looking for a way to initialize the data field using a varying size initializer list of elements T(p) containing exactly n elements. I have a feeling that it can be achieved by some construction like std::make_index_sequence, but I did not find anything appropriate.
I suppose you can use a delegating contructor, std::index_sequence and std::make_index_sequence and rewrite Array as follows (Edit: answer improved by SergeyA and Rakete1111; thanks!)
template <typename T, std::size_t N>
struct Array
{
T data[N];
template <std::size_t ... Is>
Array (Param const & p, std::index_sequence<Is...> const &)
: data { ((void)Is, T{p}) ... }
{ }
Array(const Param &p) : Array{p, std::make_index_sequence<N>{}}
{ }
};
Maybe you can make private the new constructor.
I have a templated class containing a std::array<std::vector<T>, N> data_ member.
Currently, I can construct this with
template<typename T, size_t N>
class A
{
public:
A(some parameters)
{
for (size_t n=0; n<N; n++) {
data_[n].resize(calculated size from parameters);
}
}
private:
std::array<std::vector<T>,N> data_;
};
Is there some formulation in which I can instead place the initialization of the array in the constructor?
For example, if I used only a vector and then an indexing function, I could have
...
A(some parameters):
data_(std::vector<T>(N*previous size)) {}
...
#include <array>
#include <vector>
#include <utility>
template <typename T, std::size_t N>
class A
{
public:
A(int a, int b) : A(a, b, std::make_index_sequence<N>{})
{ }
private:
template <std::size_t... Is>
A(int a, int b, std::index_sequence<Is...>)
: data_{ { std::vector<T>(((void)Is, a + b))... } }
{ }
std::array<std::vector<T>, N> data_;
};
DEMO
Since the primary container that you initialise is std::array, and since it only provides aggregate initialisation and you want to parametrise on the size of the array, I'm afraid that you can't do that the way you want. you need to generate an initialisation sequence. This is as far as I know only possible with variadic templates. I tried to put something together, but using the approach from the other answer is clearly the best choice here.
On the other hand there's always the possibility to create a static member function that returns the array as you need it:
Constructor (bool data) : member(initialiser(data)) {}
static array<vector<T>, N> initialiser(bool data) {
array<vector<T>, N> container;
// your code from above
return container;
}
Though it's debatable and dependent on the context whether this is useful or not.
I have a template class where each template argument stands for one type of value the internal computation can handle. Templates (instead of function overloading) are needed because the values are passed as boost::any and their types are not clear before runtime.
To properly cast to the correct types, I would like to have a member list for each variadic argument type, something like this:
template<typename ...AcceptedTypes> // e.g. MyClass<T1, T2>
class MyClass {
std::vector<T1> m_argumentsOfType1;
std::vector<T2> m_argumentsOfType2; // ...
};
Or alternatively, I'd like to store the template argument types in a list, as to do some RTTI magic with it (?). But how to save them in a std::initializer_list member is also unclear to me.
Thanks for any help!
As you have already been hinted, the best way is to use a tuple:
template<typename ...AcceptedTypes> // e.g. MyClass<T1, T2>
class MyClass {
std::tuple<std::vector<AcceptedTypes>...> vectors;
};
This is the only way to multiply the "fields" because you cannot magically make it spell up the field names. Another important thing may be to get some named access to them. I guess that what you're trying to achieve is to have multiple vectors with unique types, so you can have the following facility to "search" for the correct vector by its value type:
template <class T1, class T2>
struct SameType
{
static const bool value = false;
};
template<class T>
struct SameType<T, T>
{
static const bool value = true;
};
template <typename... Types>
class MyClass
{
public:
typedef std::tuple<vector<Types>...> vtype;
vtype vectors;
template<int N, typename T>
struct VectorOfType: SameType<T,
typename std::tuple_element<N, vtype>::type::value_type>
{ };
template <int N, class T, class Tuple,
bool Match = false> // this =false is only for clarity
struct MatchingField
{
static vector<T>& get(Tuple& tp)
{
// The "non-matching" version
return MatchingField<N+1, T, Tuple,
VectorOfType<N+1, T>::value>::get(tp);
}
};
template <int N, class T, class Tuple>
struct MatchingField<N, T, Tuple, true>
{
static vector<T>& get(Tuple& tp)
{
return std::get<N>(tp);
}
};
template <typename T>
vector<T>& access()
{
return MatchingField<0, T, vtype,
VectorOfType<0, T>::value>::get(vectors);
}
};
Here is the testcase so you can try it out:
int main( int argc, char** argv )
{
int twelf = 12.5;
typedef reference_wrapper<int> rint;
MyClass<float, rint> mc;
vector<rint>& i = mc.access<rint>();
i.push_back(twelf);
mc.access<float>().push_back(10.5);
cout << "Test:\n";
cout << "floats: " << mc.access<float>()[0] << endl;
cout << "ints: " << mc.access<rint>()[0] << endl;
//mc.access<double>();
return 0;
}
If you use any type that is not in the list of types you passed to specialize MyClass (see this commented-out access for double), you'll get a compile error, not too readable, but gcc at least points the correct place that has caused the problem and at least such an error message suggests the correct cause of the problem - here, for example, if you tried to do mc.access<double>():
error: ‘value’ is not a member of ‘MyClass<float, int>::VectorOfType<2, double>’
An alternate solution that doesn't use tuples is to use CRTP to create a class hierarchy where each base class is a specialization for one of the types:
#include <iostream>
#include <string>
template<class L, class... R> class My_class;
template<class L>
class My_class<L>
{
public:
protected:
L get()
{
return val;
}
void set(const L new_val)
{
val = new_val;
}
private:
L val;
};
template<class L, class... R>
class My_class : public My_class<L>, public My_class<R...>
{
public:
template<class T>
T Get()
{
return this->My_class<T>::get();
}
template<class T>
void Set(const T new_val)
{
this->My_class<T>::set(new_val);
}
};
int main(int, char**)
{
My_class<int, double, std::string> c;
c.Set<int>(4);
c.Set<double>(12.5);
c.Set<std::string>("Hello World");
std::cout << "int: " << c.Get<int>() << "\n";
std::cout << "double: " << c.Get<double>() << "\n";
std::cout << "string: " << c.Get<std::string>() << std::endl;
return 0;
}
One way to do such a thing, as mentioned in πάντα-ῥεῖ's comment is to use a tuple. What he didn't explain (probably to save you from yourself) is how that might look.
Here is an example:
using namespace std;
// define the abomination
template<typename...Types>
struct thing
{
thing(std::vector<Types>... args)
: _x { std::move(args)... }
{}
void print()
{
do_print_vectors(std::index_sequence_for<Types...>());
}
private:
template<std::size_t... Is>
void do_print_vectors(std::index_sequence<Is...>)
{
using swallow = int[];
(void)swallow{0, (print_one(std::get<Is>(_x)), 0)...};
}
template<class Vector>
void print_one(const Vector& v)
{
copy(begin(v), end(v), ostream_iterator<typename Vector::value_type>(cout, ","));
cout << endl;
}
private:
tuple<std::vector<Types>...> _x;
};
// test it
BOOST_AUTO_TEST_CASE(play_tuples)
{
thing<int, double, string> t {
{ 1, 2, 3, },
{ 1.1, 2.2, 3.3 },
{ "one"s, "two"s, "three"s }
};
t.print();
}
expected output:
1,2,3,
1.1,2.2,3.3,
one,two,three,
There is a proposal to allow this kind of expansion, with the intuitive syntax: P1858R1 Generalized pack declaration and usage. You can also initialize the members and access them by index. You can even support structured bindings by writing using... tuple_element = /*...*/:
template <typename... Ts>
class MyClass {
std::vector<Ts>... elems;
public:
using... tuple_element = std::vector<Ts>;
MyClass() = default;
explicit MyClass(std::vector<Ts>... args) noexcept
: elems(std::move(args))...
{
}
template <std::size_t I>
requires I < sizeof...(Ts)
auto& get() noexcept
{
return elems...[I];
}
template <std::size_t I>
requires I < sizeof...(Ts)
const auto& get() const
{
return elems...[I];
}
// ...
};
Then the class can be used like this:
using Vecs = MyClass<int, double>;
Vecs vecs{};
vecs.[0].resize(3, 42);
std::array<double, 4> arr{1.0, 2.0, 4.0, 8.0};
vecs.[1] = {arr.[:]};
// print the elements
// note the use of vecs.[:] and Vecs::[:]
(std::copy(vecs.[:].begin(), vecs.[:].end(),
std::ostream_iterator<Vecs::[:]>{std::cout, ' '},
std::cout << '\n'), ...);
Here is a less than perfectly efficient implementation using boost::variant:
template<typename ... Ts>
using variant_vector = boost::variant< std::vector<Ts>... >;
template<typename ...Ts>
struct MyClass {
using var_vec = variant_vector<Ts...>;
std::array<var_vec, sizeof...(Ts)> vecs;
};
we create a variant-vector that can hold one of a list of types in it. You have to use boost::variant to get at the contents (which means knowing the type of the contents, or writing a visitor).
We then store an array of these variant vectors, one per type.
Now, if your class only ever holds one type of data, you can do away with the array, and just have one member of type var_vec.
I cannot see why you'd want one vector of each type. I could see wanting a vector where each element is one of any type. That would be a vector<variant<Ts...>>, as opposed to the above variant<vector<Ts>...>.
variant<Ts...> is the boost union-with-type. any is the boost smart-void*. optional is the boost there-or-not.
template<class...Ts>
boost::optional<boost::variant<Ts...>> to_variant( boost::any );
may be a useful function, that takes an any and tries to convert it to any of the Ts... types in the variant, and returns it if it succeeds (and returns an empty optional if not).
The following code works but I would like to avoid the warning:
warning: 'fitness::vect_' should be initialized in the
member initialization list [-Weffc++]
when it is compiled with the g++ -Weffc++ switch:
#include <array>
template<class T, unsigned N>
class fitness
{
public:
explicit fitness(T v)
{
static_assert(N, "fitness zero length");
vect_.fill(v);
}
private:
std::array<T, N> vect_;
};
int main()
{
fitness<double, 4> f(-1000.0);
return 0;
}
Should I ignore the warning? Is there a way to fill vect_ in the constructor initialization list (without changing its type)?
I believe you can ignore this warning.
It works if you place an empty initialization for the array in the constructor:
#include <array>
template<class T, unsigned N>
class fitness
{
public:
explicit fitness(T v):
vect_{}
{
static_assert(N, "fitness zero length");
vect_.fill(v);
}
private:
std::array<T, N> vect_;
};
int main()
{
fitness<double, 4> f(-1000.0);
return 0;
}
Try to use
explicit fitness(T v) : vect_{}
{
//...
}
The default constructor (read: value initializer) should work fine in this case. As std::array is an aggregate type, its elements will be each be value-initialized, which for numeric types like double means zero-initialization, and then you can use fill.
explicit fitness(T v) : vect_() // or vect_{}
{
vect_.fill(v);
}
You might be better off using std::vector and its fill constructor if you don't want to do essentially double the initialization, though. Then your class would become:
template<class T>
class fitness
{
public:
explicit fitness(T v, unsigned n) : vect_(n, v)
private:
std::vector<T> vect_;
};
int main()
{
fitness<double> f(-1000.0, 4);
return 0;
}
You could, of course, still keep the N as a template parameter, but there's no need to do so as the length doesn't need to be known at compile-time. (On the other hand, if you stick with std::array you might be able to set up the constructor as a constexpr, though that may require some template fiddling or an auxiliary constexpr function that returns an initializer list to work right, I haven't played around enough with C++11 concepts to know.)
A function that generates a filled_array should have its return value be elided:
template<unsigned N, typename T>
std::array<T, N> filled_array_sized( T const& t ) {
std::array<T, N> retval;
retval.fill( t );
return retval;
}
but that requires passing in at least the size N, if not the type T.
template<typename T>
struct array_filler {
T && t;
template<typename U, unsigned N>
operator std::array<U, N>()&& {
return filled_array_sized<N, U>( std::forward<T>(t) );
}
array_filler( T&& in ):t(std::forward<T>(in)) {}
};
template<typename T>
array_filler< T >
filled_array( T&& t ) {
return array_filler<T>( t );
}
note that storing the return value of filled_array in an auto is not advised.
Use:
#include <array>
template<class T, unsigned N>
class fitness
{
public:
explicit fitness(T v): vect_( filled_array( std::move(v) ) ) {
//...
}
//...
I do not know if the above code will generate a warning in the implementation of filled_array_size, but if it does, disable the warning locally.
Here's another way, cleaner IMHO, using the C++11 non-static data member initializers:
#include <array>
template<class T, unsigned N>
class fitness
{
public:
explicit fitness(T v)
{
static_assert(N, "fitness zero length");
vect_.fill(v);
}
private:
std::array<T, N> vect_ { };
};
int main()
{
fitness<double, 4> f(-1000.0);
return 0;
}