Related
Suppose you have some hash values and want to map them to their respective strings at compile time.
Ideally, I'd love to be able to write something along the lines of:
constexpr std::map<int, std::string> map = { {1, "1"}, {2 ,"2"} };
Unfortunately, this is neither possible in C++17 nor C++2a. Nevertheless,
I tried emulating this with std::array, but can't get the size of the initializer list at compile time to actually set the type of the array correctly without explicitly specifying the size.
Here is my mockup:
template<typename T0, typename T1>
struct cxpair
{
using first_type = T0;
using second_type = T1;
// interestingly, we can't just = default for some reason...
constexpr cxpair()
: first(), second()
{ }
constexpr cxpair(first_type&& first, second_type&& second)
: first(first), second(second)
{ }
// std::pair doesn't have these as constexpr
constexpr cxpair& operator=(cxpair<T0, T1>&& other)
{ first = other.first; second = other.second; return *this; }
constexpr cxpair& operator=(const cxpair<T0, T1>& other)
{ first = other.first; second = other.second; return *this; }
T0 first;
T1 second;
};
template<typename Key, typename Value, std::size_t Size = 2>
struct map
{
using key_type = Key;
using mapped_type = Value;
using value_type = cxpair<Key, Value>;
constexpr map(std::initializer_list<value_type> list)
: map(list.begin(), list.end())
{ }
template<typename Itr>
constexpr map(Itr begin, const Itr &end)
{
std::size_t size = 0;
while (begin != end) {
if (size >= Size) {
throw std::range_error("Index past end of internal data size");
} else {
auto& v = data[size++];
v = std::move(*begin);
}
++begin;
}
}
// ... useful utility methods omitted
private:
std::array<value_type, Size> data;
// for the utilities, it makes sense to also have a size member, omitted for brevity
};
Now, if you just do it with plain std::array things work out of the box:
constexpr std::array<cxpair<int, std::string_view>, 2> mapp = {{ {1, "1"}, {2, "2"} }};
// even with plain pair
constexpr std::array<std::pair<int, std::string_view>, 2> mapp = {{ {1, "1"}, {2, "2"} }};
Unfortunately, we have to explicitly give the size of the array as second template argument. This is exactly what I want to avoid.
For this, I tried building the map you see up there.
With this buddy we can write stuff such as:
constexpr map<int, std::string_view> mapq = { {1, "1"} };
constexpr map<int, std::string_view> mapq = { {1, "1"}, {2, "2"} };
Unfortunately, as soon as we exceed the magic Size constant in the map, we get an error, so we need to give the size explicitly:
//// I want this to work without additional shenanigans:
//constexpr map<int, std::string_view> mapq = { {1, "1"}, {2, "2"}, {3, "3"} };
constexpr map<int, std::string_view, 3> mapq = { {1, "1"}, {2, "2"}, {3, "3"} };
Sure, as soon as you throw in the constexpr scope, you get a compile error and could just tweak the magic constant explicitly. However, this is an implementation detail I'd like to hide. The user should not need to deal with these low-level details, this is stuff the compiler should infer.
Unfortunately, I don't see a solution with the exact syntax map = { ... }. I don't even see light for things like constexpr auto map = make_map({ ... });. Besides, this is a different API from the runtime-stuff, which I'd like to avoid to increase ease of use.
So, is it somehow possible to infer this size parameter from an initializer list at compile time?
std::array has a deduction guide:
template <class T, class... U>
array(T, U...) -> array<T, 1 + sizeof...(U)>;
which lets you write:
// ok, a is array<int, 4>
constexpr std::array a = {1, 2, 3, 4};
We can follow the same principle and add a deduction guide for map like:
template <typename Key, typename Value, std::size_t Size>
struct map {
constexpr map(std::initializer_list<std::pair<Key const, Value>>) { }
};
template <class T, class... U>
map(T, U...) -> map<typename T::first_type, typename T::second_type, sizeof...(U)+1>;
Which allows:
// ok, m is map<int, int, 3>
constexpr map m = {std::pair{1, 1}, std::pair{1, 2}, std::pair{2, 3}};
Unfortunately, this approach requires naming each type in the initializer list - you can't just write {1, 2} even after you wrote pair{1, 1}.
A different way of doing it is to take an rvalue array as an argument:
template <typename Key, typename Value, std::size_t Size>
struct map {
constexpr map(std::pair<Key, Value>(&&)[Size]) { }
};
Which avoids having to write a deduction guide and lets you only have to write the type on the first one, at the cost of an extra pair of braces or parens:
// ok, n is map<int, int, 4>
constexpr map n{{std::pair{1, 1}, {1, 2}, {2, 3}, {3, 4}}};
// same
constexpr map n({std::pair{1, 1}, {1, 2}, {2, 3}, {3, 4}});
Note that the array is of pair<Key, Value> and not pair<Key const, Value> - which allows writing just pair{1, 1}. Since you're writing a constexpr map anyway, this distinction probably doesn't matter.
#Barry's answer pinpointed me in the right direction. Always explicitly listing pair in the list is undesirable. Moreover, I want to be able to partially specialize the template argument list of map. Consider the following example:
// for the sake of the example, suppose this works
constexpr map n({{1, "1"}, {2, "2"}});
// -> decltype(n) == map<int, const char*, 2>
// the following won't work
constexpr map<std::size_t, const char*> m({{1, "1"}, {2, "2"}});
However, perhaps the user wants that the map contains std::size_t as key, which does not have a literal. i.e. s/he would have to define a user-defined literal just to do that.
We can resolve this by offloading the work to a make_map function, allowing us to partially specialize the map:
// deduction guide for map's array constructor
template<class Key, class Value, std::size_t Size>
map(cxpair<Key, Value>(&&)[Size]) -> map<Key, Value, Size>;
// make_map builds the map
template<typename Key, typename Value, std::size_t Size>
constexpr auto make_map(cxpair<Key, Value>(&&m)[Size]) -> map<Key, Value, Size>
{ return map<Key, Value, Size>(std::begin(m), std::end(m)); }
// allowing us to do:
constexpr auto mapr = make_map<int, std::string_view>({ {1, "1"},
{2, "2"},
{3, "3"} });
I have the following class:
class Foo
{
public:
// Constructors here
private:
std::vector<X> m_data; // X can be any (unsigned) integer type
};
I want the following code to work:
Foo f0;
Foo f1(1); // and/or f1({1})
Foo f2(1, 2); // and/or f2({1, 2})
Foo f3(1, 2, 3); // and/or f3({1, 2, 3})
Foo f4(1, 2, 3, 4); // ... and so on
std::vector<int> vec = {1, 2, 3, 4};
Foo f5(vec);
Foo f6(vec.begin(), vec.end());
std::list<std::size_t> list = {1, 2, 3, 4};
Foo f7(list);
Foo f8(list.begin(), list.end());
std::any_iterable_container container = {1, 2, 3, 4};
Foo f9(container);
Foo f10(container.begin(), container.end());
// PS: I guess I only want containers/iterators that store individual
// values and not pairs (e.g., I don't want care about std::map
// because it does not make sense anyway).
So far I have tried to combine SFINAE, constructor overloading with all possible types, variadic templates, etc. Every time I fix one constructor case, others break down. Also, the code I write becomes very complex and hard to read. However, the problem seems quite simple and I guess I am just approaching it in a wrong way. Any suggestions on how to write the constructors (ideally in C++17), while also keeping the code as simple as possible, is more than welcome.
Thank you.
The simplest way to implement f1-f4 (which seem to take a variable number of arguments of a known type T that is not a container or iterator) ist this:
template<typename... Args>
Foo(T arg, Args... args) {...}
As this constructor takes at least 1 argument, there is no ambiguity with the default constructor f0. As the first argument is of type T, there is no ambiguity with the following constructors.
If you want to treat std::vector and std::list differently than other containers, you can create a partly specialized helper template to check if an argument is an instance of a given template:
template<typename>
struct is_vector : std::false_type {};
template<typename T, typename Allocator>
struct is_vector<std::vector<T, Allocator>> : std::true_type {};
And use it like this to implement f5 and f7:
template<typename T, Constraint = typename std::enable_if<is_vector<typename std::remove_reference<T>::type>::value, void>::type>
Foo(T arg) {...}
By testing for the respective iterator types of std::vector and std::list you can implement f6 and f8 in the same way.
You can check for the presence of member functions begin() and end() to implement f9 (I suppose) like this:
template<typename T>
Foo(T arg, decltype(arg.begin())* = 0, decltype(arg.end())* = 0) {...}
However, you'll have to explicitly disable this constructor for std::vector and std::list using the helper templates you created to avoid ambiguity.
To check if an argument is some iterator to implement f10, you can use std::iterator_traits:
template<typename T, typename Constraint = typename std::iterator_traits<T>::iterator_category>
Foo(T begin, T end) {...}
Again, you'll have to explicitly disable this constructor for the iterator types of std::vector and std::list.
The idea is to define the class like this:
template <typename X>
class Foo
{
public:
Foo() { };
Foo(initializer_list<int> l) :m_data(l) { };
template<typename container>
Foo(container const & c) :m_data(c.begin(), c.end()) {};
template<typename iterator>
Foo(iterator begin, iterator end) :m_data(begin, end) { };
private:
std::vector<X> m_data;
};
Where:
Foo() is the default (non-parametric) constructor.
Foo(initializer_list<int> l) accepts a list like {1, 2, 3}.
Foo(container const & c) accepts any container that supports begin and end iterators.
Foo(iterator begin, iterator end) initializes the class with begin and end iterators.
Usage:
Foo<int> f0;
Foo<int> f1({1});
Foo<int> f2({1, 2});
Foo<int> f3({1, 2, 3});
Foo<int> f4({1, 2, 3, 4});
std::vector<int> vec = {1, 2, 3, 4};
Foo<int> f5(vec);
Foo<int> f6(vec.begin(), vec.end());
std::list<size_t> list = {1, 2, 3, 4};
Foo<size_t> f7(list);
Foo<size_t> f8(list.begin(), list.end());
set<unsigned> container = {1, 2, 3, 4};
Foo<unsigned> f9(container);
Foo<unsigned> f10(container.begin(), container.end());
Assuming the class is defines as following:
template <class T>
class Foo
{
public:
[..]
private:
std::vector<T> m_data;
}
Let's break this task into sub-tasks:
Construct from iterators
template <class Iterator>
Foo (Iterator begin, Iterator end, typename Iterator::iterator_category * = 0)
: m_data(begin, end);
We will fill in our m_data from begin and end.
The third parameter will make sure only Iterator types that declare iterator_category will match this prototype. Since this argument has a default value of 0 and is never specified, it serves a purpose only during the template deduction process. When the compiler checks if this is the right prototype, if the type Iterator::iterator_category doesn't exist, it will skip it. Since iterator_category is a must-have type for every standard iterator, it will work for them.
This c'tor will allow the following calls:
std::vector<int> vec = {1, 2, 3, 4};
Foo<int> f(vec.begin(), vec.end());
-- AND --
std::list<std::size_t> list = {1, 2, 3, 4};
Foo<int> f(list.begin(), list.end());
Construct from container
template <class Container>
Foo (const Container & container, decltype(std::begin(container))* = 0, decltype(std::end(container))* = 0)
: m_data(std::begin(container), std::end(container));
We will fill our m_data from the given container. We iterate over it using std::begin and std::end, since they are more generic than their .begin() and .end() counterparts and support more types, e.g. primitive arrays.
This c'tor will allow the following calls:
std::vector<int> vec = {1, 2, 3, 4};
Foo<int> f(vec);
-- AND --
std::list<std::size_t> list = {1, 2, 3, 4};
Foo<int> f(list);
-- AND --
std::array<int,4> arr = {1, 2, 3, 4};
Foo<int> f(arr);
-- AND --
int arr[] = {1, 2, 3, 4};
Foo<int> f(arr);
Construct from an initializer list
template <class X>
Foo (std::initializer_list<X> && list)
: m_data(std::begin(list), std::end(list));
Note: We take the list as an Rvalue-reference as it's usually the case, but we could also add a Foo (const std::initializer_list<X> & list) to support construction from Lvalues.
We fill in our m_data by iterating over the list once again. And this c'tor will support:
Foo<int> f1({1});
Foo<int> f2({1, 2});
Foo<int> f3({1, 2, 3});
Foo<int> f4({1, 2, 3, 4});
Constructor from variable number of arguments
template <class ... X>
Foo (X ... args) {
int dummy[sizeof...(args)] = { (m_data.push_back(args), 0)... };
static_cast<void>(dummy);
}
Here, filling in the data into the container is a bit trickier. We use parameter expansion to unpack and push each of the arguments. This c'tor allows us to call:
Foo<int> f1(1);
Foo<int> f2(1, 2);
Foo<int> f3(1, 2, 3);
Foo<int> f4(1, 2, 3, 4);
Entire class
The final result is quite nice:
template <class T>
class Foo
{
public:
Foo () {
std::cout << "Default" << std::endl;
}
template <class ... X>
Foo (X ... args) {
int dummy[sizeof...(args)] = { (m_data.push_back(args), 0)... };
static_cast<void>(dummy);
std::cout << "VA-args" << std::endl;
}
template <class X>
Foo (std::initializer_list<X> && list)
: m_data(std::begin(list), std::end(list)) {
std::cout << "Initializer" << std::endl;
}
template <class Container>
Foo (const Container & container, decltype(std::begin(container))* = 0, decltype(std::end(container))* = 0)
: m_data(std::begin(container), std::end(container)) {
std::cout << "Container" << std::endl;
}
template <class Iterator>
Foo (Iterator first, Iterator last, typename Iterator::iterator_category * = 0)
: m_data(first, last) {
std::cout << "Iterators" << std::endl;
}
private:
std::vector<T> m_data;
};
I have a vector of ints that I want to add multiple values too but too many values to add using a lot of push_backs. Is there any method of adding multiple values at the end of a vector. Something along the lines of this:
std::vector<int> values
values += {3, 9, 2, 5, 8, etc};
I found that boost has something like this, but I would like not having to include boost.
#include <boost/assign/std/vector.hpp>
using namespace boost::assign;
{
std::vector<int> myElements;
myElements += 1,2,3,4,5;
}
Which seems to be declared like this:
template <class V, class A, class V2>
inline list_inserter<assign_detail::call_push_back<std::vector<V,A> >, V>
operator+=( std::vector<V, A>& c, V2 v )
{
return push_back( c )( v );
}
Is there any C++/C++11 way to do this or, if not, how would it be implemented?
This should work:
std::vector<int> values;
values.insert( values.end(), { 1, 2, 3, 4 } );
Perhaps with insert:
values.insert( values.end(), {3, 9, 2, 5, 8, etc} );
Demo.
In order to present as much as possible solutions, this should work too:
for(const auto x : {11, 12, 13, 14})
v.push_back(x);
Demo.
You can just make an operator:
template <class T>
std::vector<T>& operator+=(std::vector<T>& lhs, std::initializer_list<T> l)
{
lhs.insert(std::end(lhs), l);
return lhs;
}
You can mimic the boost boost::assign behavior
template <typename T>
class vector_adder
{
public:
std::vector<T>& v;
vector_adder(std::vector<T>& v):v(v)
{ }
vector_adder& operator,(const T& val)
{
v.push_back(val);
return *this;
}
};
template <typename T>
vector_adder<T> operator+=(std::vector<T>& v,const T& x)
{
return vector_adder<T>(v),x;
}
Then,
std::vector<int> v {1,2,3,4};
v += 11,12,13,14 ;
See here
You can do it by using the insert member function, as such:
vector<int> v;
int myarr[] {1, 2, 4, 5, 5, 6, 6, 8}; //brace initialization with c++11
v.insert(v.end(), myarr, myarr+8);
There was a previous answer that omitted the middle argument in the insert parameters, but that one does not work. There are several formats to follow for the insert method, which can be found here and the code snippet I wrote practically follows this format:
vectorName.insert(postion to start entering values, first value to enter, how many values to enter)
Note: That the last two arguments (i.e. myarr, myarr+8) use pointer arithmetics. myarr is the address in memory of the first element in the array, and the second value is the address of the 8th element. You can read about pointer arithmetic here.
Hope that helps!
I have an integer array as shown:
int ia[] = {1, 2, 3, 4, 5, 6};
I want to convert it to a list<int> and a vector<int>. The obvious way that comes to my mind is iterating over the array, and add the elements to the list<int> and vector<int>:
for (auto val: ia) {
ilist.push_back(val);
ivec.push_vack(val);
}
I just wanted to know, whether there is any other way, probably any available library function?
You can use a two-iterator constructor:
std::list<int> ilist(std::begin(ia), std::end(ia));
std::vector<int> ivec(std::begin(ia), std::end(ia));
If you don't have C++11 support for std::begin and std::end, you can use
std::list<int> ilist(ia, ia + 6);
where in real code you would aim to provide an array length function instead of using 6 explicitly.
Better still, you could role out your own begin and end function templates, for example
template< class T, std::size_t N >
T* my_end( const T (&a)[N] )
{
return &a[N];
}
Edit here's an array length function template:
template< class T, size_t N >
std::size_t size( const T (&)[N] )
{
return N;
}
When I transform code from C to C++ I sometimes encounter language constructs that are C, but compatible with C++. Usually I want to transform the code in the least intrusive way. But I have one case where I find that very difficult:
In C you can declare an array and initializing... well... parts of it using "designators", the rest is zeroed out (Edit: I wrote "left to randomness" here, first):
int data[7] = {
[2] = 7,
[4] = 9,
};
This is not valid C++-code, though (luckily). So I will have to use a different strategy.
While I can see a non-intrusive way in C++11:
static const map<int,int> data = { {2,7}, {4,9} };
what should I do when C++11-features are not available yet?
Can I circumvent a runtime initialization?
Is there a way to initialize similar kind of mapping in a "literal" way?
What is least intrusive to the code that uses data?
Well unless the size of the array is totally insane, you can always do this
int data[7] = {
0,
0,
7, // #2
0,
9 // #4
// the rest will be 0-initialized
};
Works in compile time too
If uniform initialization is not available, the std::map<int, int> could be initialized using boost::assign::map_list_of:
#include <boost/assign/list_of.hpp>
static const std::map<int,int> data = boost::assign::map_list_of(2,7)(4,9);
Rather than using map<int, int> you can backport std:array (or a minimal equivalent) from C++11, and use a Boost.Assign-style builder facility:
#include <cstddef>
template<typename T, size_t N> struct array { T data[N]; };
template<typename T, size_t N> struct build_array: public array<T, N> {
build_array &operator()(size_t i, const T &t) {
this->data[i] = t;
return *this;
}
};
array<int, 7> data_array = build_array<int, 7>()(2, 7)(4, 9);
int (&data)[7] = data_array.data;
Why can't you do:
int data[7];
data[2] = 7;
data[4] = 9;
looks very-very similar =)
If you do not want to use boost::assign
You can create it's simple analogue:
template<class T1, class T2>
std::map<T1, T2> cre(std::map<T1, T2> & m)
{
return std::map<T1, T2>();
}
template<class T1, class T2>
std::map<T1, T2> & ass(std::map<T1, T2> & m, T1 const & p1, T2 const & p2)
{
m[p1] = p2;
return m;
}
std::map<int, int> data = ass(ass(cre(data), 2, 3), 7, 6);