This question already has answers here:
Initializing a static std::map<int, int> in C++
(12 answers)
Closed 5 years ago.
So for some reason this works in a class contructor but not outside a class I was wondering as to why and how could I get my map to work outside a class.
#include <iostream>
#include <string>
#include <map>
typedef std::map <std::string, int> idMap;
idMap type_id;
type_id["Moon"] = 1;
type_id["Star"] = 2;
type_id["Sun"] = 3;
int main()
{
std::cout << type_id["Moon"] << std::endl;
}
The compiler errors I get are as follows
11:1: error: 'type_id' does not name a type 12:1: error: 'type_id' does not name a type 13:1: error: 'type_id' does not name a type
I am looking for an example like this that would work, also if you can tell me why this won't work.
Your main should look like this:
int main()
{
type_id["Moon"] = 1;
type_id["Star"] = 2;
type_id["Sun"] = 3;
std::cout << type_id["Moon"] << std::endl;
}
You cannot put these statements outside of a function (in that case main()).
Or if you really want to populate your map outside main(), you could it by using a list initializer constructor, like this:
idMap type_id { {"Moon", 1}, {"Star", 2}, {"Sun", 3} };
This approach works for a header file as well, like this:
myHeader.h
#include <string>
#include <map>
typedef std::map <std::string, int> idMap;
idMap type_id { {"Moon", 1}, {"Star", 2}, {"Sun", 3} };
main.cpp
#include <iostream>
#include "myHeader.h"
int main() {
std::cout << type_id["Moon"] << std::endl;
}
Related
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
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
This question already has an answer here:
Why can in-class initializers only use = or {}? [duplicate]
(1 answer)
Closed 5 years ago.
This is an example of a simplified problem that I try to resolve.
#include <iostream>
#include <set>
using namespace std;
auto comp = [](int x, int y) { return x < y; };
struct Struct {
set<int, decltype(comp)> member(comp);
};
int main() {
Struct variable;
variable.member.insert(1);
variable.member.insert(5);
for(auto v: variable.member) {
cout << v << endl;
}
return 0;
}
I am using the following command to compile my code:
g++ -std=c++14 -o test.exe test.cpp
Unfortunately, it keeps producing following error:
test.cpp:9:34: error: unknown type name 'comp'
set<int, decltype(comp)> reads(comp);
^
I have tried to install newer version of g++, but it doesn't work.
I executed this command on MacOS with Clang, as well as Ubuntu 16 with g++ in version 5.2.
struct Struct {
set<int, decltype(comp)> member{ comp };
};
My visual studio 2017 complains that 'comp' is not a type name and it can not see the definition of 'member', so I guess the compiler recognizes 'member' as a function definition.
Use member{ comp } seems solves this problem
Without structure this example can be compiled as:
#include <iostream>
#include <set>
using namespace std;
auto& comp = [](int x, int y){ return x < y; };
auto member = set<int, decltype(comp)>(comp);
int main() {
member.insert(1);
member.insert(5);
for (auto v : member) {
cout << v << endl;
}
return 0;
}
UPDATE:
For case with structure, try:
#include <iostream>
#include <set>
using namespace std;
auto& comp = [](int x, int y){ return x < y; };
struct Struct {
set<int, decltype(comp)> member = set<int, decltype(comp)>(comp);
} variable;
int main() {
variable.member.insert(1);
variable.member.insert(5);
for (auto v : variable.member) {
cout << v << endl;
}
return 0;
}
My Visual Studio 2013 cannot compile auto member = set<int, decltype(comp)>(comp); insude structure - error is error C2853: 'member' : a non-static data member cannot have a type that contains 'auto'
This question already has answers here:
Undefined reference to static class member
(9 answers)
Closed 5 years ago.
I'd like to access a static map that is stored in a seperate class and iterate through and print the contents.
So I have three files:
HasGlobals.cpp which has the global map.
/* HasGlobals.cpp */
#include <string>
#include <map>
#include <iostream>
class HasGlobals{
public:
static std::map<int, std::string> globalData;
HasGlobals(){
globalData.insert(std::pair<int, std::string>(0, "data"));
};
~HasGlobals(){
};
};
WantsGlobals.cpp which has a method that needs to use the map from HasGlobals.cpp for one of its methods.
/* WantsGlobals.cpp */
#include "HasGlobals.cpp"
#include <string>
class WantsGlobals{
public:
WantsGlobals(){
};
~WantsGlobals(){
};
//THIS IS THE METHOD THAT I CAN'T GET TO WORK
void printContentsOfGlobal(){
HasGlobals * globalRetriever = new HasGlobals();
for(std::map<int, std::string>::iterator it = globalRetriever->globalData.begin(); it != globalRetriever->globalData.end(); ++it){
std::cout << it->first << " " << it->second << std::endl;
}
};
};
Main.cpp which simply tries to call the printContentsOfGlobal() method in WantsGlobals.cpp.
/* Main.cpp */
#include <iostream>
#include "WantsGlobals.cpp"
int main(){
WantsGlobals * globalRetriever = new WantsGlobals();
globalRetriever->printContentsOfGlobal();
}
Right now this code doesn't compile and gives me
undefined reference to 'HasGlobals::globalData'
How would I implement printContentsOfGlobal() so that I can retrieve the data in the map[globalData] in the seperate HasGlobals.cpp file?
EDIT:
Thanks for the help everyone. Below is the code I created with everyone's help. Yes it's still not perfect but it compiles and performs the desired behavior:
HasGlobals.hpp
#include <string>
#include <map>
#include <iostream>
class HasGlobals{
public:
static std::map<int, std::string> globalData;
HasGlobals(){};
~HasGlobals(){};
static std::map<int, std::string> createDummyData(){
std::map<int, std::string> m;
m[0] = "Help";
m[1] = "Me";
m[2] = "Stackoverflow";
m[3] = "You're my only hope.";
return m;
}
};
std::map<int, std::string> HasGlobals::globalData = HasGlobals::createDummyData();
WantsGlobals.hpp
#include "HasGlobals.hpp"
#include <string>
#include <map>
class WantsGlobals{
public:
WantsGlobals(){};
~WantsGlobals(){};
void printContentsOfGlobal(){
for(std::map<int, std::string>::iterator it = HasGlobals::globalData.begin(); it != HasGlobals::globalData.end(); ++it){
std::cout << it->first << " " << it->second << std::endl;
}
};
};
Main.cpp
#include <iostream>
#include <"WantsGlobals.hpp">
int main(){
WantsGlobals * globalRetriever = new WantsGlobals();
globalRetriever->printContentsOfGlobal();
}
(CW as this is now just hints & tips on a dupe, left for posterity.)
Why are you #includeing .cpp files? Don't do that. It looks like those two files should be named .hpp instead.
You're close. Since the map is static, you don't need an instance of HasGlobals to access it, so HasGlobals * globalRetriever = new HasGlobals(); is pointless (plus you leaked it).
The map is "called" HasGlobals::globalData.
Though you will need to find some place to add data to it; the HasGlobals constructor is not the proper place, as that is called every time you instantiate the class. Best to use the initialiser when you define the object, which you forgot to do, hence the "undefined reference" linker error you mentioned in comments.
You are using the map correctly from then on, except from a typo here:
std::map<int, std:;string>
// ^
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
-