I have a class containing a std::array of QVariant:
class MyClass()
{
typedef std::array<QVariant, 42> attribs_t;
attribs_t m_Attribs;
public:
MyClass()
{
m_Attribs[0] = 41;
}
};
I now analyzed this project using Intel Parallel inspector and it detects
"uninitialized memory access" at m_Attribs[0] = 41;
Do i have to call the constructor of the elements in std::array manually or is this a false positive?
The size of a std::array array is fixed at compile-time, and the default constructor (which your MyClass constructor invokes) default-constructs or copy-constructs every QVariant of the array. I.e. I'd expect that at the beginning of your constructor, there are 42 QVariant objects in your array and they are all invalid (i.e. isValid() returns false).
The assignment should have the same effect as
m_Attribs[0] = QVariant( 41 );
which seems okay to me.
In short: I'd tend to claim it's a false positive if it wasn't for Intel's good reputation. :-}
I think your code is fine.
Sample code:
#include <iostream>
#include <array>
struct QVariant {
QVariant () : v_(0) { std::cout << "Constructed" << std::endl; }
QVariant & operator=(int x) { std::cout << "Assigned" << std::endl;
v_ = x; return *this; }
int v_;
};
class MyClass {
typedef std::array<QVariant, 5> attribs_t; // 5 instead of 42
attribs_t m_Attribs;
public:
MyClass() {
m_Attribs[0] = 41;
}
};
int main(int argc, const char* argv[]) {
MyClass mc;
return 0;
}
should print:
Constructed
Constructed
Constructed
Constructed
Constructed
Assigned
And does (on gcc and clang)
Related
Suppose I have class A and two derived classes, B and C, e.g.:
#include <iostream>
#include <list>
#include <string>
using namespace std;
class A {
public:
virtual void poke() const = 0;
virtual ~A() {};
};
class B : public A {
string _response;
public:
B(const string& response) : _response(response) {}
void poke () const {
cout << _response << endl;
}
};
class C : public A {
string _response;
public:
C(const string& response) : _response(response) {}
void poke () const {
cout << "Well, " << _response << endl;
}
};
Can I somehow initialize an std::list using the following initializer list: {B("Me"), C("and you")}, so that polymorphism works when I iterate over the list and call poke() (i.e., no slicing occurs)? I guess I need to define an std::list<Smth>, where Smth accepts temporary objects, has a copy constructor that does move semantics inside (because initialization lists seem to be doing copying and not moving), and supports smart pointers so I can iterate with it and do (*it)->poke(). Just for clarity, I want to be be able to write:
list<Smth> test {B("Me"), C("and you")};
for(auto it = test.begin(); it != test.end(); it++) {
(*it)->poke();
}
I was trying to find a simple solution but I got to the point where my program compiled but generated run time errors, and so I gave up at that point... Maybe somehow make a unique pointer out of a temporary object? Or can I use && somehow?
For polymorphism, you need a reference or a pointer. Both will become dangling as soon as the sentence ends, because even if you bound those objects to them somehow, you bound them to temporary objects.
The usual solution is to dynamically allocate and create the objects and holding them with pointers. This means something like the following (I also changed the loop to C++11 style, instead of using iterators directly):
std::list<std::unique_ptr<A>> test {
std::make_unique<B>("Me"), std::make_unique<C>("and you")};
for(const auto& p : test) {
p->poke();
}
Well, if I am willing to do extra copying of B and C and use a shared_ptr instead of a unique_ptr, then the following example works (I am not saying it is good programming style, but it does show the cost of having a convenient notation with initialization lists):
#include <iostream>
#include <list>
#include <memory>
#include <string>
using namespace std;
class A {
public:
virtual void poke() const = 0;
virtual ~A() {}
};
class B : public A {
string _response;
public:
B(const string& response) : _response(response) {}
void poke () const {
cout << _response << endl;
}
operator shared_ptr<A>() {
return make_shared<B>(*this);
}
};
class C : public A {
string _response;
public:
C(const string& response) : _response(response) {}
void poke () const {
cout << "Well, " << _response << endl;
}
operator shared_ptr<A>() {
return make_shared<C>(*this);
}
};
int main() {
list<shared_ptr<A>> test {B("Me"), C("and you")};
for(const auto& it : test) {
it->poke();
}
}
I am still little bit confused by returning a const reference. Probably, this has been already discussed, however let's have following code as I did not find the same:
#include <vector>
#include <iostream>
struct A
{
int dataSize;
std::vector<char> data;
};
class T
{
public:
T();
~T();
const A& GetData();
private:
A dataA;
};
T::T() : dataA{1}
{
}
T::~T()
{
}
const A& T::GetData()
{
return dataA;
}
void main()
{
T t;
A dataReceivedCpy = {};
dataReceivedCpy = t.GetData();
const A& dataReceivedRef = t.GetData();
std::cout << dataReceivedRef.dataSize << std::endl;
}
What exactly happens, when I call
dataReceivedCpy = t.GetData();
Is this correct? From my point of view, it is and a copy of the requested struct is made, am I right?
On the other hand,
const A& dataReceivedRef = t.GetData();
returns a reference to object member, it is correct, unless the T object is not destructed. Am I right?
Yes, your understanding sounds correct.
dataReceivedCpy = t.GetData();
calls a copy assignment operator to put a copy of t.dataA in dataReceivedCpy.
const A& dataReceivedRef = t.GetData();
does no copying and makes dataReceivedRef a reference to t.dataA. It is valid for the lifetime of t.
Reading Scott Meyer's book "Effective Modern C++", Item 24 (and following), and Item 41, I wonder that this book opposes:
the individual constructors for lvalue and rvalue parameters
to
a template'd universal constructor solution
It says, that 1. has the disadvantage to duplicate code.
Whereas 2. has the disadvantage to potentially being used for unwanted types.
I wonder why the book does not mention a mixed model - as in the example code shown below.
It uses type-safe dedicated constructors for lvalue and rvalue but delegates to a single (private) generic implementation for "universal reference".
This avoids unwanted template types of a public "universal reference" constructor.
So is there is anything wrong with the approach below? Something I missed?
#include <iostream>
#include <string>
class MyClass
{
private:
enum class Dummy { Nop = 0 } ;
template <class T>
MyClass(Dummy, T&& data)
: _data(std::forward<T>(data))
{
std::cout << "MyClass universal reference template c'tor" << std::endl;
}
public:
// proxy c'tors delegating to universal reference c'tor
MyClass (std::string const & data)
: MyClass(Dummy::Nop, data)
{
std::cout << "MyClass lvalue c'tor" << std::endl;
}
MyClass (std::string && data)
: MyClass(Dummy::Nop, std::move(data))
{
std::cout << "MyClass rvalue c'tor" << std::endl;
}
private:
std::string _data;
};
int main(int, char**)
{
{
std::string str("demo");
MyClass myClass(str);
}
{
MyClass myClass("hello, world");
}
return 0;
}
And now let's put the book down and do it the right way:
Pros:
Optimal efficiency
Correct type limitations
DRY
Cons:
None
-
#include <iostream>
#include <string>
#include <type_traits>
class MyClass
{
public:
template <class T, std::enable_if_t<std::is_constructible<std::string, T>::value>* = nullptr>
MyClass(T&& data)
: _data(std::forward<T>(data))
{
std::cout << "MyClass universal reference template c'tor" << std::endl;
}
private:
std::string _data;
};
int main()
{
using namespace std::string_literals;
auto a = MyClass("hello"s);
auto b = MyClass("world");
const auto s = "Hello, World"s;
auto s2 = "Hello, World";
auto c = MyClass(s);
auto d = MyClass(s2);
// won't compile
// auto e = MyClass(10);
}
I'm not sure of the title, because I'm not sure the issue comes from the "copyablility" of my container.
I tryied quite everything but I can't get rid of this error.
Here is a simplified version of my code (please do not challenge the class design, I really would like to keep the end-used syntax in the BOOST_FOREACH):
template <typename T>
class MyContainer
{
public:
typedef typename std::vector<T>::iterator iterator;
typedef typename std::vector<T>::const_iterator const_iterator;
MyContainer(std::vector<T>& vec, boost::mutex& mutex) :
m_vector(vec),
m_lock(mutex)
{
}
iterator begin() { return m_vector.begin(); }
const_iterator begin() const { return m_vector.begin(); }
iterator end() { return m_vector.end(); }
const_iterator end() const { return m_vector.end(); }
private:
std::vector<T>& m_vector;
boost::lock_guard<boost::mutex> m_lock;
};
template <typename T>
struct GetContainer
{
GetContainer(std::vector<T>& vec, boost::mutex& mutex) :
m_vector(vec),
m_mutex(mutex)
{
}
MyContainer<T> Get()
{
return MyContainer<T>(m_vector, m_mutex);
}
std::vector<T>& m_vector;
boost::mutex& m_mutex;
};
int main()
{
std::vector<int> v;
v.push_back(1);
v.push_back(2);
boost::mutex m;
GetContainer<int> getter(v, m);
BOOST_FOREACH(int i, getter.Get())
{
std::cout << i << std::endl;
}
return 0;
}
The compiler complains about not having a copy constructor for MyContainer::MyContainer(const MyContainer&).
I also have :
error: no matching function for call to ‘MyContainer::MyContainer(boost::foreach_detail_::rvalue_probe >::value_type)’
I follow the extensibility tips:
http://www.boost.org/doc/libs/1_58_0/doc/html/foreach/extensibility.html#foreach.extensibility.making__literal_boost_foreach__literal__work_with_non_copyable_sequence_types
But, making
MyContainer<T> : private boost::noncopyable
doesn't solve the issue.
Nor defining the function
boost_foreach_is_noncopyable
or specializing the template struct
is_noncopyable
for MyContainer (in fact, how would I specialize this template for a template type ?)
Last "tip":
If I remove the mutex and the lock from everywhere (I just pass the vector to GetContainer and to MyContainer), it works.
But it doesn't work if I make
MyContainer<T> : private boost::noncopyable
(I expected it should, so I'm not sure my problem is with BOOST_FOREACH, but maybe because I return a copy of MyContainer with my getter ?)
I thank you if you read me until here, and thanks in advance for help.
Seems to be a limitation of BOOST_FOREACH with move-only types. I didn't find a way around it¹ (except for the - ugly - obvious approach to put the lock_guard in a shared_ptr).
You didn't specify a c++03 requirement, though, so you can make it work without BOOST_FOREACH by replacing lock_guard with unique_lock.
Here's my take on things in c++11 (note how generic it is):
Live On Coliru
#include <boost/thread.hpp>
#include <boost/range.hpp>
namespace detail {
template <typename R, typename M>
struct RangeLock {
RangeLock(R&r, M& m) : _r(r), _l(m) {}
RangeLock(RangeLock&&) = default;
using iterator = typename boost::range_iterator<R>::type;
iterator begin() { using std::begin; return begin(_r); }
iterator end () { using std::end; return end (_r); }
using const_iterator = typename boost::range_iterator<R const>::type;
const_iterator begin() const { using std::begin; return begin(_r); }
const_iterator end () const { using std::end; return end (_r); }
private:
R& _r;
boost::unique_lock<M> _l;
};
}
template <typename R, typename M>
detail::RangeLock<R,M> make_range_lock(R& r, M& mx) { return {r,mx}; }
template <typename R, typename M>
detail::RangeLock<R const,M> make_range_lock(R const& r, M& mx) { return {r,mx}; }
#include <vector>
#include <map>
int main() {
boost::mutex mx;
std::vector<int> const vec { 1, 2 };
std::map<int, std::string> const map { { 1, "one" }, { 2, "two" } };
for(int i : make_range_lock(vec, mx))
std::cout << i << std::endl;
for(auto& p : make_range_lock(map, mx))
std::cout << p.second << std::endl;
for(auto& p : make_range_lock(boost::make_iterator_range(map.equal_range(1)), mx))
std::cout << p.second << std::endl;
}
Prints
1
2
one
two
one
¹ not even using all the approaches from Using BOOST_FOREACH with a constant intrusive list
I post my answer if it can help...
With C++03, I finally provide a copy constructor to be able to use the class with BOOST_FOREACH.
So the issue is moved to another topic: make the class copied in a logic and suitable way.
In my case, I "share the lock and the vector", the user shouldn't use this copy itself if he doesn't want to do bugs, but in BOOST_FOREACH it's okay:
I change the mutex to a recursive_mutex
I change the lock to an unique_lock
and:
MyContainer(const MyContainer& other) :
m_vector(other.vec),
m_lock(*other.m_lock.mutex())
{
}
With C++11
Thanks to Chris Glover on the boost mailling list, a C++11 solution:
You can't do what you are trying to do in C++03. To accomplish it, you
need C++11 move semantics to be able to move the MyContainer out of the Get
function. Even without using BOOST_FOREACH, the following code fails;
GetContainer<int> getter(v, m);
MyContainer<int> c = getter.Get(); // <-- Error.
Here's an example with the necessary changes; I changed the scoped_lock to
a unique_lock and added a move constructor.
template <typename T>
class MyContainer
{
public:
[...]
MyContainer(MyContainer&& other)
: m_vector(other.m_vector)
{
m_lock = std::move(other.m_lock);
other.m_vector = nullptr;
}
Would the following be an idiomatic C++11 implementation of a Scope Guard that restores a value upon scope exit?
template<typename T>
class ValueScopeGuard
{
public:
template<typename U>
ValueScopeGuard(T& value, U&& newValue):
_valuePtr(&value),
_oldValue(std::forward<U>(newValue))
{
using std::swap;
swap(*_valuePtr, _oldValue);
}
~ValueScopeGuard()
{
if(_valuePtr)
{
using std::swap;
swap(*_valuePtr, _oldValue);
}
}
// Copy
ValueScopeGuard(ValueScopeGuard const& other) = delete;
ValueScopeGuard& operator=(ValueScopeGuard const& other) = delete;
// Move
ValueScopeGuard(ValueScopeGuard&& other):
_valuePtr(nullptr)
{
swap(*this, other);
}
ValueScopeGuard& operator=(ValueScopeGuard&& other)
{
ValueScopeGuard(std::move(other)).swap(*this);
return *this;
}
private:
T* _valuePtr;
T _oldValue;
friend void swap(ValueScopeGuard& lhs, ValueScopeGuard& rhs)
{
using std::swap;
swap(lhs._valuePtr, rhs._valuePtr);
swap(lhs._oldValue, rhs._oldValue);
}
};
template<typename T, typename U>
ValueScopeGuard<T> makeValueScopeGuard(T& value, U&& newValue)
{
return {value, std::forward<U>(newValue)};
}
It could be used to temporarily change a value as follows:
int main(int argc, char* argv[])
{
// Value Type
int i = 0;
{
auto guard = makeValueScopeGuard(i, 1);
std::cout << i << std::endl; // 1
}
std::cout << i << std::endl; // 0
// Movable Type
std::unique_ptr<int> a{new int(0)};
{
auto guard = makeValueScopeGuard(a, std::unique_ptr<int>{new int(1)});
std::cout << *a << std::endl; // 1
}
std::cout << *a << std::endl; // 0
return 0;
}
Is a simple utility like this already implemented in a library somewhere? I had a look at Boost.ScopeExit, but its intended usage seems different and more complex.
Assuming makeValueScopeGuard to be implemented as :
template< typename T >
ValueScopeGuard<T> makeValueScopeGuard( T& t, T&& v )
{
return ValueScopeGuard<T>(t,std::move(v));
}
no, it is not very good implementation of scope guard, because it is going to fail when you pass l-values as the 2nd parameter :
int kk=1;
auto guard = makeValueScopeGuard(i, kk);
The second problem is that you used std::forward, when you should have used std::move.
As this question and answers show, people are usually using lambdas to implement scope guard.
Your move constructor leaves the pointer member uninitialized, so the rvalue object ends up holding a junk pointer, which it dereferences in its destructor. That's a bug. You should initialize it to nullptr and check for nullptr in the destructor.
For a type like this I would not expect move assignment to be a simple swap, I would expect the rvalue to end up not owning anything. So I would implement the move like this instead, so the rvalue ends up empty:
ValueScopeGuard& operator=(ValueScopeGuard&& other)
{
ValueScopeGuard(std::move(other)).swap(*this);
return *this;
}
The name makeValueScopeGuard isn't clear to me that it changes the value itself, I'd expect it to just copy the current value and restore it in the destructor.
As far as existing types go, the closest I can think of is the Boost I/O state savers, which do not alter the current state they just copy it and restore it.