how can I list-initialize my own class? - c++

I want my own class can be list-initialized like vector:
myClass a = {1, 2, 3};
How can I do that using C++11 capabilities?

C++11 has a notion of initializer lists. To use it, add a constructor which accepts a single argument of type std::initializer_list<T>. Example:
#include <vector>
#include <initializer_list>
#include <iostream>
struct S
{
std::vector<int> v_;
S(std::initializer_list<int> l)
: v_(l)
{
std::cout << "constructed with initializer list of length " << l.size();
}
};
int main()
{
S s = { 1, 2, 3 };
return 0;
}

An initializer_list can be (like other STL containers) iterated and the size can be queried. This allows you to do as pointed in the other answer by yuyoyuppe to immediately feed it to a vector. But you may have other intents for your initialization and do something like execute an operation directly on the items of the list without copying.
#include <initializer_list>
#include <iostream>
#include <algorithm>
struct S
{
int thesum;
int items;
S(std::initializer_list<int> l) :
thesum(std::accumulate(l.begin(), l.end(), 0)),
items(l.size())
{}
};
int main()
{
S s = { 1, 2, 3 };
std::cout << s.items << std::endl;
std::cout << s.thesum << std::endl;
return 0;
}
This prints
3
6
See:
http://www.cplusplus.com/reference/initializer_list/initializer_list/?kw=initializer_list
http://en.cppreference.com/w/cpp/utility/initializer_list
-

Related

C++ const std::array size from constructor

Say I get an int from a lambda function ran at initialization of a class object. Is it possible to use that int to define the size of a std::array? Something like the following code.
#include <array>
#include <vector>
#include <iostream>
class Test1 {
public:
Test1( std::vector<int> vec1 ) :
nvars([&vec1]() -> int { return vec1.size()+1; }())
{
};
const int nvars;
// ******This isn't allowed!!!!!!!!!
const std::array<int,nvars> arr;
};
int main() {
std::vector<int> vec{1,2,3,4};
Test1 test1(vec);
std::cout << "nvars: " << test1.nvars << std::endl;
return 0;
}
I am a C++ beginner so any other advice will be welcome.
No. The size of the array is part of its type. You cannot let it be determined at runtime.
You can have it be determined at compile time, if you do pass a std::array to the constructor. Since C++17 there is CTAD (class template argument deduction) which lets you write:
#include <array>
template <size_t N>
class Test1 {
public:
Test1( std::array<int,N> vec1 ) :
arr(vec1)
{
};
const std::array<int,N> arr;
};
int main() {
std::array vec{1,2,3,4};
Test1 test1(vec);
}
Live Demo
test1 is of type Test1<4>. Note that Test1<4> is a distinct different type than eg Test<5> or Test<24>. If you want one type to have a member array of different size, make it a std::vector.

How to Initialize a vector<typeinfo>?

I am able to do this:
std::vector<int> vec = { 1, 2, 3, 4, 5 };
But I am not able to do this:
std::vector<const type_info&> ClassBlackList = { typeid(Class1), typeid(Class2) };
compiler says pointer to reference is illegal
or
std::vector<const type_info> ClassBlackList = { typeid(Class1), typeid(Class2) };
compiler says Error C2338 The C++ Standard forbids containers of const elements because allocator is ill-formed.
or
std::vector<type_info> ClassBlackList = { typeid(Class1), typeid(Class2) };
Compiler says:
Error C2280 'type_info::type_info(const type_info &)': attempting to reference a deleted function
I am able not able to do push_back either.
What is the solution to have a vector or list of type_info?
You can use pointers
std::vector<const std::type_info*> v = { &typeid(Class1), &typeid(Class2) };
This is valid because typeid returns a reference to an object with static storage duration.
You cannot have a vector of references, for several fundamental reasons. C++ simply does not work this way. You can, however, employ std::reference_wrapper to get pretty much the same result:
#include <functional>
#include <vector>
#include <typeinfo>
class A {
};
int main()
{
std::vector<std::reference_wrapper<const std::type_info>> avec;
auto &t=typeid(A);
avec.push_back(t);
const std::type_info &i=avec[0];
return 0;
}
You can't have arrays of references so you could wrap them in std::reference_wrappers:
#include <functional>
#include <typeinfo>
#include <vector>
std::vector<std::reference_wrapper<const std::type_info>> ClassBlackList = {
typeid(Class1),
typeid(Class2)
};
The name ClassBlackList implies that you will search it a lot and also that the elements in the list are to be unique. In that case, you may want to use a std::set instead.
Example:
#include <functional>
#include <iostream>
#include <typeinfo>
#include <set>
struct Class1 {};
struct Class2 {};
struct Class3 {};
struct comp { // a functor to compare reference wrapped type_info's
std::size_t operator()(const std::reference_wrapper<const std::type_info>& lhs,
const std::reference_wrapper<const std::type_info>& rhs) const
{
return std::less<const std::type_info*>{}(&lhs.get(), &rhs.get());
}
};
int main() {
std::set<std::reference_wrapper<const std::type_info>, comp> ClassBlackList = {
typeid(Class1),
typeid(Class2)
};
// try to insert typeid(Class3) twice, it only succeeds the first time
auto[it1, inserted1] = ClassBlackList.insert(typeid(Class3));
std::cout << "inserted: " << inserted1 << '\n';
auto[it2, inserted2] = ClassBlackList.insert(typeid(Class3));
std::cout << "inserted: " << inserted2 << '\n';
}
Output:
inserted: 1
inserted: 0

What does this map initialization mean in C++ STL?

int numSubarraysWithSum(vector<int>& A, int S) {
unordered_map<int, int> c({{0, 1}});// Line 1
int psum = 0, res = 0;
for (int i : A) {
psum += i;
res += c[psum - S];
c[psum]++;
}
return res;
}
What does line 1 mean? I'm confused as there are two curly braces.
It's something called an Initilizer list. According to this website:
Initializer List is used in initializing the data members of a class.
The list of members to be initialized is indicated with constructor as
a comma-separated list followed by a colon.
Basically it adds elements into your std::map (or in this case your std::unordered_map) right while you're creating it.
More specifically, from #Aconcagua comment above :
The outer braces define
the initializer list itself, the inner braces are shorthand for
constructing std::pairs, which are used in the std::map to store the keys
and corresponding values.
You can see this from this piece of code:
#include <iostream>
#include <map>
#include <unordered_map>
using namespace std;
int main()
{
map<int, int> c({{0, 1}, {1, 7}});
for (auto it = c.begin(); it!=c.end(); it++)
{
cout << it->first << " " << it->second << "\n";
}
}
Output:
0 1
1 7
There's also other data types that support this, for example std::vector :
#include <iostream>
#include <vector>
using namespace std;
int main()
{
vector<int> test({0, 7, 9, 11, 3});
for (auto x : test) {cout << x << "\n";}
}
Output:
0
7
9
11
3
There's a lot of question regarding this:
Initializing a static std::map<int, int> in C++
What would a std::map extended initializer list look like?
And more info: https://en.cppreference.com/w/cpp/utility/initializer_list
*Cautions : As #Aconcagua mentioned below, using namespace std is not considered a good practice, although it might save some typing time.
It simply just add elements into your map. There are many different ways to do this:
unordered_map<int, int> c{ {0, 1} }; similar to your question
2 unordered_map<int, int> c;
c[0]=1;
assign key and value
3.
unordered_map<int, int> c;
c.insert(std:: make_pair (key, vale) ); use make_pair
... You can refer more here map

How to move a unique_ptr from one set to another? (C++)

The code below is giving the error: Call to deleted constructor of 'std::unique_ptr<int>' 'unique_ptr' has been explicitly marked deleted here passing argument to parameter 'item' here.
Could someone please explain why this is? I would have thought everything would be fine because I'm using std::move in the call to foo.add.
#include <iostream>
#include <memory>
#include <set>
class Foo {
public:
void add(std::unique_ptr<int> item) {
set.emplace(std::move(item));
}
private:
std::set<std::unique_ptr<int>> set;
};
int main() {
Foo foo;
std::set<std::unique_ptr<int>> set;
set.emplace(std::make_unique<int>(1));
set.emplace(std::make_unique<int>(2));
set.emplace(std::make_unique<int>(3));
for (auto &item : set) {
foo.add(std::move(item)); // error on this line
}
return 0;
}
Use c++ 17 extract() function.
example
#include <set>
#include <memory>
#include <iostream>
int main() {
auto s = std::set<std::unique_ptr<int>>{};
s.insert(std::make_unique<int>(10));
std::cout << s.size() << "\n";
auto it = s.extract(s.begin());
// Pointer type here just for clarification
std::unique_ptr<int> new_ptr = std::move(it.value());
std::cout << s.size() << "\n";
std::cout << *new_ptr << "\n";
}
Then instead of your for each loop you might use a while loop:
while (!set.empty()) {
auto it = set.extract(set.begin());
foo.add(std::move(it.value());
}

Expand variadic arguments in initializer list fails

I try to create a simple program in which I want to create vector of future arguments.
I created a wrapper function which is used to submit lambda functions and stores internally in a vector the future objects
I use an intermediate step in which I create an initiliazer_list using variadic arguments . But fails to compile. I try to use to call a function in order to push the elements in the vector and fails to compile as well
Below is the code
#include <iostream>
#include <thread>
#include <future>
#include <functional>
#include <cstdlib>
#include <chrono>
#include <initializer_list>
using namespace std;
using FunctPtr = function<int(int, int) >;
using FutureInt = future<int>;
using AsyncVector = vector<FutureInt>;
AsyncVector asyncVec;
template<typename... TemplatePtr>
void submit(TemplatePtr... pFunc)
{
auto initList {pFunc... };
for (auto & element : initList)
{
asyncVec.emplace_back(async(launch::async, element,4,5));
}
}
int main()
{
int a;
int b;
auto addPtr = [](int x, int y)->int
{
std::cout << "add :" << x + y << std::endl;
return x + y;
};
auto multPtr = [](int x, int y)->int
{
std::cout << "mult :" << x * y << std::endl;
return x * y;
};
// submit(add,4,5);
submit(addPtr, multPtr);
for (auto & v : asyncVec)
{
std::cout << "Wait for " << v.get() << std::endl;
}
}
Yes, they are of different types so cannot be in the same init-list easily.
Your best options should probably be:
Either push them all into asyncVec in the same fold-expression.
template<typename... TemplatePtr>
void submit(TemplatePtr... pFunc)
{
(asyncVec.emplace_back(async(launch::async, std::move(pFunc), 4, 5)), ...);
}
Or, if they all are of the same signature, type-erase them, like keeping them in an array of std::function.
template<typename... TemplatePtr>
void submit(TemplatePtr... pFunc)
{
for (auto &&element: {std::function<int(int, int)>(std::move(pFunc))...})
{
asyncVec.emplace_back(async(launch::async, std::move(element), 4, 5));
}
}
(I have specified function signature explicitly though compiler should be able to deduce it.)
Or, if all closures are captureless and of the same signature, simply cast them to the same type when calling submit:
using SameType = int (*)(int, int);
submit(static_cast<SameType>(addPtr), static_cast<SameType>(mulPtr));
This way your original submit should work as is.