std::array derived class aggregate initialization - c++

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
}

Related

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.

Constructor with template arguments

I have a Dynamic class that can store different types : int, double, std::vector<int>, std::vector<double>, etc. I have about 50 of such types.
I would like my Dynamic type to have a constructor where we give two informations:
The type to be stored
Arguments used to construct the type inside the Dynamic class
I am looking forward to something such as
const Dynamic x<std::vector<double>>{10};
to construct in place a Dynamic object that has a std::vector<double> of length 10.
PS: I am allowed to use C++11 and I am not allowed to use RTTI
Constructor template arguments must be deduced. They cannot be provided explicitly. You can get around this by providing a type tag which encodes the wanted template argument and passing it as an additional constructor argument. For example :
#include <utility> // For std::forward
struct foo
{
// Helper tag type
template<class T>
struct type_tag {};
// The template argument T is deduced from type_tag<T>
template<class T, class ... Args>
foo(type_tag<T>, Args&&... p_args)
{
T value{ std::forward<Args>(p_args)... };
}
};
int main()
{
// Provide a type tag so the template argument can be deduced
foo bar{ foo::type_tag<int>{}, 5 };
}
As long as you don't mind putting the type info next to Dynamic rather than the variable name you can do this with variadic args:
#include <iostream>
#include <vector>
template <typename T>
class Dynamic
{
public:
template <typename... Args>
Dynamic(Args... args) : data_(args...)
{
}
T data_;
};
int main()
{
const Dynamic<std::vector<double>> x{10};
std::cout << x.data_.size() << std::endl;
}

c++ parameter pack specification in constructor rather than template

Unlike function declarations with parameter packs, I've found that classes require the type for each argument in the angle brackets...
Component<IntegerPair, int, int> temp(40, 5);
...which seems redundant. Here's how I defined Component:
template<typename T, class... T_Args>
class Component
{
public:
Component(T_Args... args)
: m_data(args...)
{}
T m_data;
};
Is there a way to remove int, int from the above statement?
If so, is it ok to remove it?
Also, is my way of instantiation m_data safe? When using
std::forward<T_Args>(args)... my compiler told me I didn't have a
constructor that could convert all of the argument types.
One way is to make the constructor a template:
#include <utility>
struct IntegerPair {
IntegerPair(int, int) {}
};
template<typename T>
class Component
{
public:
template<typename... T_Args>
Component(T_Args&&... args)
: m_data(std::forward<T_Args>(args)...)
{}
T m_data;
};
int main()
{
Component<IntegerPair> c {1,2};
}
This is functionally equivalent to std::vector and its member function emplace_back. It's perfectly ok, IMO. The error messages are pretty cryptic, as usual in template constructs like this, but this can be mitigated with an appropriate static_assert.
template parameter deduction only work for function calls so the basic pattern to achieve what you want looks like this:
template<typename T, class... T_Args>
Component<T, T_Args...> makeComponent(T_Args&&... args) {
return Component<T, T_Args...>(std::forward<T_Args>(args)...);
}
Usage:
auto c = makeComponent<IntegerPair>(1, 1)

How to parameterize the number of parameters of a constructor?

I want to accept up to a number of parameters (this number being defined in a template parameter) in a template class constructor. I can't use an initializer_list, since I can't assert its size at compile time, as far as I know.
What I tried
My first attempt was using a std::array as a parameter:
template<size_t s>
class foo {
int v[s];
public:
foo(std::array<int, s>) {/*...*/}
};
However, that forces me to initialize like this (even when the constructor is not explicit) :
foo<4> a{{1,2,3,4}} // Two brackets.
I would think there may be some template magic (variadic templates?), but I can't even figure out the proper syntax to use in the constructor. I can't call a constructor recursively... can I?
I tried looking for a definition of the constructor for std::array(since it doesn't allow for more parameters than the size of the array, just what I want), but all I could find is that it has implicit constructors. Is that the default constructors? If so, how does
std::array<int, 3> a = {1,2,3}
work?
Optional bonus: Why didn't the standard define a fixed size alternative to std::initializer_list? Something like std::static_initializer_list<T, N>. Are there any plans on supporting this kind of functionality in the future? Is it even needed?
You could create a variadic constructor and just assert that it was provided the right number of arguments:
template <size_t SZ>
struct Foo {
template <typename... Args>
Foo(Args... args) {
static_assert(sizeof...(Args) <= SZ, "Invalid number of arguments");
// ... stuff ...
}
};
So that:
Foo<3> f; // OK
Foo<3> f(1, 2, 3); // OK
Foo<3> f(1, 2, 3, 4, 5); // error
As an example to initialize an array, that could look like:
template <size_t SZ>
struct Foo {
template <typename... Args>
Foo(Args... args)
: v{{args...}}
{
static_assert(sizeof...(Args) <= SZ, "Invalid number of arguments");
}
std::array<int, SZ> v;
};
That constructs v correctly as you'd expect, though if you try to pass more than SZ args to Foo's constructor, you'd see the error on initializing v before the static_assert.
For a clearer static_assert error, you could delegate the top-level Foo to private constructors that take an extra integral_constant argument for whether or not they're valid constructors:
template <typename... Args>
Foo(Args... args)
: Foo(std::integral_constant<bool, sizeof...(Args) <= SZ>{},
args...)
{ }
private:
template <typename... Args>
Foo(std::true_type, Args... args)
: v{{args...}}
{ }
template <typename False, typename... Args>
Foo(False, Args... )
{
// False is only ever std::false_type
static_assert(False::value, "Invalid number of arguments!");
}

Template classes with specialised constructors

Consider the following contrived example of a templated array definition:
template <typename t, unsigned int n> class TBase
{
protected:
t m_Data[n];
//...
};
template <typename t, unsigned int n> class TDerived : public TBase<t, n>
{
TDerived()
{
}
};
I can specialize this type to provide a non-default constructor for an array of length 2 as follows:
template <typename t> class TDerived<t, 2> : public TBase<t, 2>
{
public:
TDerived(const t& x0, const t& x1)
{
m_Data[0] = x0;
m_Data[1] = x1;
}
};
int main()
{
TDerived<float, 2> Array2D_A(2.0f, 3.0f); //uses specialised constructor
TDerived<float, 3> Array3D_A; //uses default constructor
return 0;
}
Is there some other way I can create a class that has different constructor options constrained against template parameters at compile-time without the requirement for a complete class specialisation for each variation?
In other words, is there some way I can have specialised constructors in the TBase class without the need for the intermediary step of creating TDerived whilst preserving the functionality of TBase?
I think deriving your class from a base class is not relevant to the question here, that's a mere implementation detail. What you really seem to be after is if there's a way to partially specialize member functions, like the constructor. Do you want something like this?
template <typename T, int N> class Foo
{
Foo(); // general
template <typename U> Foo<U, 2>(); // specialized, NOT REAL CODE
};
This doesn't work. You always have to specialize the entire class. The reason is simple: You have to know the full type of the class first before you even know which member functions exist. Consider the following simple situation:
template <typename T> class Bar
{
void somefunction(const T&);
};
template <> class Bar<int>
{
double baz(char, int);
};
Now Bar<T>::somefunction() depends on T, but the function only exists when T is not int, because Bar<int> is an entirely different class.
Or consider even another specialization template <> class Bar<double> : public Zip {}; -- even the polymorphic nature of a class can be entirely different in a specialization!
So the only way you can provide specializations new declarations of members, including constructors, is by specializing the entire class. (You can specialize the definition of existing functions, see #Alf's answer.)
There are basically two options I see for this:
Use a variadic function for construction (ie. "..." notation), you can use the value n inside that function to get your arguments from the stack. However, the compiler will not check at compile time if the user provides the correct number of arguments.
Use some serious template magic to allow a call chaning initialization, that would look like this: vector(2.0f)(3.0f). You can actually build something that at least ensures the user does not provide too many arguments here. However tha mechanism is a little more involved, I can assemble an example if you want.
You can always specialize a member, e.g.
#include <stdio.h>
template< class Type >
struct Foo
{
void bar() const
{ printf( "Single's bar.\n" ); }
};
template<>
void Foo< double >::bar() const
{ printf( "double's bar.\n" ); }
int main()
{
Foo<int>().bar();
Foo<double>().bar();
}
But you want effectively different signatures, so it's not directly a case of specializing a member.
One way forward is then to declare a constructor with a single argument, of a type dependent on the template parameters.
Then you can specialize that, as you want.
Cheers & hth.,
Since constructor is a function, you need to fully specialize the containing class to address your specific problem. No way out.
However, functions cannot be partially specialized (in all compilers). So suppose if you know that you need n = 2 when t = int or double then following is one alternative.
template<>
TDerived<int,2>::TDerived()
{
//...
}
template<>
TDerived<double,2>::TDerived()
{
//...
}
and so on.
[Note: If you use MSVC, then I think it supports partial specialization; in that case you can try:
template<typename t>
TDerived<t,2>::TDerived()
{
//...
}
though, I am not sure enough for that.]
You could give the most common definitions in the non-specialized class and static_assert (BOOST_STATIC_ASSERT for non C++0x) on the array length. This could be considered a hack but is a simple solution to your problem and safe.
template<typename T, unsigned int n>
struct Foo {
Foo(const T& x) { static_assert(n == 1, "Mooh!"); }
Foo(const T& x1, const T& x2) { static_assert(n == 2, "Mooh!"); }
};
The "evil" way would be variadic arguments.
template<typename T, unsigned int n>
struct Foo {
Foo(...) {
va_list ap;
va_start(ap, n);
for(int j=0; j < n; ++j)
bork[j] = va_arg(ap, T);
va_end(ap);
}
};
Then there is also C++0x and the good old make_something trick which is more difficult then one would think.
template<typename... T, unsigned int n>
Foo<T, n> make_foo(T&&...) {
// figure out the common_type of the argument list
// to our Foo object with setters or as a friend straight to the internals
Foo< std::common_type< T... >::type, sizeof(T) > foo;
// recursive magic to pick the list apart and assign
// ...
return foo;
}