I'm having the following code, but after run the code, the result is empty, any ideas why the result is empty? the reference of result in function main was passed to myclass, I thought function addToResult will actually add data to result, and I'm expecting a map key = "test", value = "1": "1". I'm kind of new to c++. Thanks!
#include <iostream>
#include <string>
#include <unordered_map>
using LookUpTable = std::unordered_map<std::string, std::string>;
using DLTable = std::unordered_map<std::string, LookUpTable>;
class MyClass
{
public:
MyClass(DLTable& dltable) {
m_dltable = dltable;
};
void addToResult() {
LookUpTable ee;
ee.emplace("1", "1");
m_dltable.emplace("test", ee);
};
private:
DLTable m_dltable;
};
int main ()
{
DLTable result;
MyClass myclass(result);
myclass.addToResult();
std::cout << "myrecipe contains:" << std::endl;
for (auto& x: result) {
std::cout << x.first << ": "<< std::endl;
for (auto& xx : x.second) {
std::cout << xx.first << ": " << xx.second << std::endl;
}
}
std::cout << std::endl;
return 0;
}
Let' look into simplified example:
int a = 0;
int &b = a;
int c = b;
c = 123;
Will last assignment modify a? Of course not. It does not matter how you pass value to c through reference or not c is completely independent variable that just initialized by a reference.
Your case is the same - m_dltable is separate variable and the fact you initialize it using reference does not change anything. (Your case even worse, you did not initialize it by reference, you assigned to it)
In general your approach is wrong. If you want directly access that variable then just make it public, do not try to create convoluted workarounds on how to access it. If you want incapsulation just create members that allow you to iterate over that container. For example return a const reference to it or have begin() and end() methods that return (const) iterators accordingly.
Related
I'm trying to initialize a tuple (using std::make_tuple), and I want to store an std::type_info in it as well, but I don't know why for some reason this simple looking piece of code doesn't compile.
I tried running the following code:-
#include <iostream>
#include <string>
#include <tuple>
#include <typeinfo>
#include <any>
int main()
{
std::tuple<std::type_info, int, std::any> tpl = std::make_tuple(typeid(float), 1, "Hello");
const auto&[type, num, anyval] = tpl;
std::cout<<type.name()<<std::endl;
std::cout<<num<<std::endl;
std::cout<<std::any_cast<const char*>(anyval)<<std::endl;
return 0;
}
If I only omit storing the first value (ie. type info) and create only std::tuple<int, std::any> there doesn't seem to be any problem, but this attempt fails. How to get around it, as I've to store the std::type_info object anyhow.
Problem is that std::type_info do not have ANY constructors. No copy, no move or default constructors. So you can't copy this value. You can get only reference of it.
You can use std::reference_wrapper as a workarund:
int main()
{
std::tuple<std::reference_wrapper<const std::type_info>, int, std::any> tpl = std::make_tuple(std::cref(typeid(float)), 1, "Hello");
const auto& [type, num, anyval] = tpl;
std::cout << type.get().name() << std::endl;
std::cout << num << std::endl;
std::cout << std::any_cast<const char*>(anyval) << std::endl;
return 0;
}
https://godbolt.org/z/czobc3h57
As point out by StoryTeller there is std::type_index to address this issue in nicer way:
int main()
{
std::tuple<std::type_index, int, std::any> tpl = std::make_tuple(std::type_index(typeid(float)), 1, "Hello");
const auto& [type, num, anyval] = tpl;
std::cout << type.name() << std::endl;
std::cout << num << std::endl;
std::cout << std::any_cast<const char*>(anyval) << std::endl;
return 0;
}
https://godbolt.org/z/z5dv8Wf86
I overlooked this, but now I've a solution which might be even better.
I skipped storing std::type_info (or std::type_index) at all. The third type (std::any) contains an std::type_info for the type it is enclosing, so storing it twice wouldn't be a good thing anyways.
Although the standard library does provide std::type_index which I didn't knew but came across while researching for this a bit.
I'm trying to use views in a commercial application, and noticed an inconsistency between gcc and Visual Studio.
In the code below, calling transformed() twice returns two different, apparently incompatible views. In gcc 11 (on godbolt), the code executes without issue, even with extra debugging, but in Visual Studio 16.11 with -std:c++latest, it asserts:
cannot compare incompatible transform_view iterators
I would like my function to be callable just as if it were returning a const std::vector<std::pair<int, int>> & so the caller doesn't have to worry about temporaries. It seems that I could make my transformed view a member of my class, initialize it in the constructor, and return that, but I don't even know how to declare it.
I'm assuming that Visual Studio is correct and my code is illegal, but even if my code should be legal, it still has to work. We have a 10,000,000-line code base and a lot of non-expert C++ programmers, and I need the core to be robust and not have hidden gotchas like this.
#include <iostream>
#include <ranges>
#include <vector>
struct X
{
std::vector<int> m_values{ 1,2,3 };
auto transformed() const
{
return std::ranges::views::transform(m_values, [](int i) {
return std::pair{ i, i + i };
});
}
};
int main()
{
X x;
for (auto [a, b] : x.transformed())
std::cout << a << " " << b << std::endl;
if (x.transformed().begin() != x.transformed().end()) // asserts in visual studio.
std::cout << "not empty";
return 0;
}
https://godbolt.org/z/hPWYGn9dY
It seems that I could make my transformed view a member of my class,
initialize it in the constructor, and return that, but I don't even
know how to declare it.
You can turn X into a template class, and construct the member transform_view through the passed lambda, something like this:
#include <iostream>
#include <ranges>
#include <vector>
template<class F>
struct X {
std::vector<int> m_values{1,2,3};
decltype(m_values | std::views::transform(std::declval<F>())) m_transformed;
X(F fun) : m_transformed(m_values | std::views::transform(std::move(fun))) { }
const auto& transformed() const { return m_transformed; }
};
int main() {
X x([](int i) { return std::pair{i, i + i}; });
for (auto [a, b] : x.transformed())
std::cout << a << " " << b << std::endl;
if (x.transformed().begin() != x.transformed().end())
std::cout << "not empty";
}
Demo.
Another way is to use std::function, which makes your X need not be a template class:
struct X {
using Fun = std::function<std::pair<int, int>(int)>;
std::vector<int> m_values{1,2,3};
decltype(m_values | std::views::transform(Fun{})) m_transformed;
X(Fun fun = [](int i) { return std::pair{i, i + i}; })
: m_transformed(m_values | std::views::transform(std::move(fun))) { }
const auto& transformed() const { return m_transformed; }
};
Demo.
Let's define a class inside a free function, and access it outside:
#include <iostream>
auto myFunc(){
class MyType{public: int i = 0; int j = 1;};
return MyType();
}
int main() {
auto my_type = myFunc();
std::cout << my_type.i << " " << my_type.j << "\n";
return 0;
}
It compiles, run as expected:
0 1
The name MyType is properly hidden:
if we replace auto, the following won't compile:
int main() {
MyType my_type = myFunc();
std::cout << my_type.i << " " << my_type.j << "\n";
return 0;
}
What does the standard say about it?
How to prevent it? The following code did not help:
namespace{
auto myFunc(){
class MyType{public: int i = 0; int j = 1;};
return MyType();
}
}
int main() {
auto my_type = myFunc();
std::cout << my_type.i << " " << my_type.j << "\n";
// your code goes here
return 0;
}
The standard doesn't say anything about this specifically, except that — as you've already pointed out — it's the name that has a scope, not the type. Use of auto bypasses the type's name, giving you a way to get at the type regardless of the name's scope.
It's kind of similar to how making a nested class private doesn't mean you can't use instances of it, only that you can't name it outside of the encapsulating class's scope.
I don't see how you'd "prevent" it, nor why you'd want to.
I know that in general the life time of a temporary in a range-based for loop is extended to the whole loop (I've read C++11: The range-based for statement: "range-init" lifetime?). Therefore doing stuff like this is generally OK:
for (auto &thingy : func_that_returns_eg_a_vector())
std::cout << thingy;
Now I'm stumbling about memory issues when I try to do something I thought to be similar with Qt's QList container:
#include <iostream>
#include <QList>
int main() {
for (auto i : QList<int>{} << 1 << 2 << 3)
std::cout << i << std::endl;
return 0;
}
The problem here is that valgrind shows invalid memory access somewhere inside the QList class. However, modifying the example so that the list is stored in variable provides a correct result:
#include <iostream>
#include <QList>
int main() {
auto things = QList<int>{} << 1 << 2 << 3;
for (auto i : things)
std::cout << i << std::endl;
return 0;
}
Now my question is: am I doing something dumb in the first case resulting in e.g. undefined behaviour (I don't have enough experience reading the C++ standard in order to answer this for myself)? Or is this an issue with how I use QList, or how QList is implemented?
Since you're using C++11, you could use initialization list instead. This will pass valgrind:
int main() {
for (auto i : QList<int>{1, 2, 3})
std::cout << i << std::endl;
return 0;
}
The problem is not totally related to range-based for or even C++11. The following code demonstrates the same problem:
QList<int>& things = QList<int>() << 1;
things.end();
or:
#include <iostream>
struct S {
int* x;
S() { x = NULL; }
~S() { delete x; }
S& foo(int y) {
x = new int(y);
return *this;
}
};
int main() {
S& things = S().foo(2);
std::cout << *things.x << std::endl;
return 0;
}
The invalid read is because the temporary object from the expression S() (or QList<int>{}) is destructed after the declaration (following C++03 and C++11 §12.2/5), because the compiler has no idea that the method foo() (or operator<<) will return that temporary object. So you are now refering to content of freed memory.
The compiler can't possibly know that the reference that is the result of three calls to operator << is bound to the temporary object QList<int>{}, so the life of the temporary is not extended. The compiler does not know (and can't be expected to know) anything about the return value of a function, except its type. If it's a reference, it doesn't know what it may bind to. I'm pretty sure that, in order for the life-extending rule to apply, the binding has to be direct.
This should work because the list is no longer a temporary:
#include <iostream>
#include <QList>
int main() {
auto things = QList<int>{};
for (auto i : things << 1 << 2 << 3)
std::cout << i << std::endl;
return 0;
}
And this should work because the binding is direct, so the rule can apply:
#include <iostream>
#include <QList>
int main() {
for (auto i : QList<int>{1, 2, 3})
std::cout << i << std::endl;
return 0;
}
Consider the following code:
#include <iostream>
#include <map>
class Value
{
public:
void set(const int intValue){ intValue_ = intValue; }
int read() const { return intValue_; }
void replaceIfInMap(){
std::map<int,int>::iterator it;
it = valuesToReplace_->find(intValue_);
if(it != valuesToReplace_->end()){
intValue_ = it->second;
}
}
Value(std::map<int,int>* valuesToReplace) : valuesToReplace_(valuesToReplace){}
private:
std::map<int,int>* valuesToReplace_;
int intValue_;
};
class Holder {
public:
void doStuffWithValues(){
Value a(&valuesToReplace_), b(&valuesToReplace_), c(&valuesToReplace_);
a.set(1); b.set(2); c.set(3);
valuesToReplace[2]=5;
a.replaceIfInMap(); b.replaceIfInMap(); c.replaceIfInMap();
std::cout << "a: " << a.read()
<< " b: " << b.read()
<< " c: " << c.read() << std::endl;
}
private:
std::map<int,int> valuesToReplace_;
};
int main()
{
Holder holder;
holder.doStuffWithValues();
}
How could I get access to the valuesToReplace_ member in a more convenient (and preferably more elegant) way? I have considered storing the map as a public static member of the class Value, but that would deny the possibility of having multiple instances of the Holder class, as each Holder instance requires a set of Value instances with different replacement settings.
A global map would be an even uglier "solution"...
Calling Value::read() from Holder and doing the map interaction there is not an option as this code is only a simplification and in the real code the equivalent of each instance of Value can store pointers to other instances of the same class rendering the aforementioned method overly complex and bulky.
Why does the above code even work? Holder::valuesToReplace_ is private! Is this just normal C++ behaviour (as you cannot get that pointer without access to the private members of the class anyway)?
Why does the above code even work? Holder::valuesToReplace_ is
private!
It is private, so Holder::doStuffWithValues can access it because it is a member function, nothing wrong there.
Value a(&valuesToReplace_), b(&valuesToReplace_), c(&valuesToReplace_);
a.set(1); b.set(2); c.set(3);
Here, all your Value objects have valuesToReplace_ pointing to the same map is that what you want? It seems strange, I would either have a static map (which would make a copy on assignment) or a smart pointer to prevent unexpected deletion (but allow NULL values).
How could I get access to the valuesToReplace_ member in a more
convenient (and preferably more elegant) way?
You could keep it private and have public member functions which return begin/end const_iterators for the map, or setIntForInt/getIntForInt accessor methods which are not dependent on internal implementation.
How about passing a reference to the valuesToReplace map to your replaceIfInMap method?
class Value
{
public:
void set(const int intValue){ intValue_ = intValue; }
int read() const { return intValue_; }
void replaceIfInMap(std::map<int,int> const& valuesToReplace_){
std::map<int,int>::const_iterator it;
it = valuesToReplace_->find(intValue_);
if(it != valuesToReplace_->end()){
intValue_ = it->second;
}
}
Value() {}
private:
int intValue_;
};
class Holder {
public:
void doStuffWithValues(){
Value a, b, c;
a.set(1); b.set(2); c.set(3);
valuesToReplace_[2]=5;
a.replaceIfInMap(valuesToReplace_);
b.replaceIfInMap(valuesToReplace_);
c.replaceIfInMap(valuesToReplace_);
std::cout << "a: " << a.read()
<< " b: " << b.read()
<< " c: " << c.read() << std::endl;
}
private:
std::map<int,int> valuesToReplace_;
};