In my comp sci class we are making a vector class. I am stuck because my copy constructor is initializing my 'limit' variable with a garbage value and I dont understand why.
Here is my main:
myStringVector v1 {"a", "b", "c"};
std::cout << "make it here?" << std::endl;
myStringVector v2 = v1;
std::cout << v1[0] << v1[1] << v1[2] << std::endl;
std::cout << v2[0] << v2[1] << v2[2] << std::endl;
assert(v1 == v2);
std::cout << "Passed copy ctor" << std::endl;
Here is the constructor that takes an initializer_list
myStringVector::myStringVector(std::initializer_list<myString> list){
this->reserve(list.size());
for(int i = 0; i < limit-1; i++){
construct(first+i, list.begin()[i]);
}
}
Here is the copy constructor
myStringVector::myStringVector(const myStringVector& data){
reserve((data.limit)-1);
for(int i = 0; i < data.limit-1; i++){
construct(&first+i, data.first+i);
}
}
Here is what reserve looks like
void myStringVector::reserve(int n){
this->first = allocate<myString>(n);
this->last = first+n;
this->limit = n+1;
}
My assertion in main is failing because I overloaded the == operator to first check if the limits are equal. If they are not, the function returns false. The function continues after that, but it is failing the assertion because I am getting my v1 to be 4, which is correct, and then my v2 is some large number that doesn't make sense.
I've tried rewriting my copy constructor to directly initialize the members of the class without using reserve and it still doesn't work. It is still assigning garbage values to limit. The rest of it is working fine.
Here is what main is outputting...
make it here?
abc
abc
4 7500072
Assertion failed: v1 == v2, file myCodeTres.cpp, line 58
I feel like I've tried everything I can think of to try and fix this but nothings working. Any advice for me?
Thanks.
Update: these are the compiler error I get when I leave the ampersand out.
In file included from myCodeTres.cpp:20:
myMemory.hpp: In instantiation of 'T* construct(T*, Args&& ...) [with T = myString; Args = {myString*}]':
myStringVector.cpp:25:36: required from here
myMemory.hpp:61:10: error: no matching function for call to 'myString::myString(myString*)'
61 | return new (p) T(std::forward(args)...);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
**then there are some weird errors that are in blue on the compiler screen **
In file included from myCodeTres.cpp:23:
myString.cpp:42:1: note: candidate: 'myString::myString(myString&&)'
42 | myString::myString(myString&& move) : strLength(strlen(move.stringVar)) //move constructor to move contents from
| ^~~~~~~~
myString.cpp:42:31: note: no known conversion for argument 1 from 'myString*' to 'myString&&'
42 | myString::myString(myString&& move) : strLength(strlen(move.stringVar)) //move constructor to move contents from
| ~~~~~~~~~~~^~~~
myString.cpp:32:1: note: candidate: 'myString::myString(const myString&)'
32 | myString::myString(const myString& strToCpy) : strLength(strlen(strToCpy.stringVar)) //copy constructor that removes old elements
| ^~~~~~~~
myString.cpp:32:36: note: no known conversion for argument 1 from 'myString*' to 'const myString&'
32 | myString::myString(const myString& strToCpy) : strLength(strlen(strToCpy.stringVar)) //copy constructor that removes old elements
| ~~~~~~~~~~~~~~~~^~~~~~~~
In file included from myCodeTres.cpp:23:
myString.cpp:23:1: note: candidate: 'myString::myString(const char*, std::size_t)'
23 | myString::myString(const char* cPnt, std::size_t size) : strLength(size) //constructor to take a size and const ptr to char
| ^~~~~~~~
myString.cpp:23:1: note: candidate expects 2 arguments, 1 provided
myString.cpp:14:1: note: candidate: 'myString::myString(const char*)'
14 | myString::myString(const char* cPnt) : strLength(strlen(cPnt)) //constructor to initialize to a given const ptr to char.
| ^~~~~~~~
myString.cpp:14:32: note: no known conversion for argument 1 from 'myString*' to 'const char*'
14 | myString::myString(const char* cPnt) : strLength(strlen(cPnt)) //constructor to initialize to a given const ptr to char.
| ~~~~~~~~~~~~^~~~
myString.cpp:8:1: note: candidate: 'myString::myString()'
8 | myString::myString() //default constructor: initializes empty string.
| ^~~~~~~~
myString.cpp:8:1: note: candidate expects 0 arguments, 1 provided
update: whole class definition
#ifndef MYSTRINGVECTOR_HPP
#define MYSTRINGVECTOR_HPP
#include "myString.hpp"
#include <initializer_list>
class myStringVector{
private:
myString *first, *last;
int limit = 0;
public:
void reserve(int); //allocate memory at 'first' for n myString's
myStringVector(); //call to reserve(0)
myStringVector(std::initializer_list<myString>);
myStringVector(const myStringVector&);
bool empty();
bool operator==(const myStringVector&);
myString operator[](int);
int getSize() const;
};
#endif //MYSTRINGVECTOR_HPP_INCLUDED
.cpp file
#include "myStringVector.hpp"
#include "myMemory.hpp"
#include <initializer_list>
void myStringVector::reserve(int n){
this->first = allocate<myString>(n);
this->last = first+n;
this->limit = n+1;
}
myStringVector::myStringVector(){
this->reserve(0);
}
myStringVector::myStringVector(std::initializer_list<myString> list){
this->reserve(list.size());
for(int i = 0; i < limit-1; i++){
construct(first+i, list.begin()[i]);
}
}
myStringVector::myStringVector(const myStringVector& data){
reserve((data.limit)-1);
for(int i = 0; i < data.limit-1; i++){
construct(first+i, data.first+i);
}
}
bool myStringVector::empty(){
return (limit-1 == 0);
}
bool myStringVector::operator==(const myStringVector& data){
std::cout << limit << " " << data.limit << std::endl;
if(this->limit != data.limit)
return 0;
for(int i = 0; i < limit; i++){
if(*(first+i) != *(data.first+i))
return 0;
}
return 1;
}
myString myStringVector::operator[](int index){
return *(first+index);
}
int myStringVector::getSize() const{
return limit-1;
}
formatting is a bit off because uploading code to stackoverflow is tedious as can be.
Simple error, in your copy constructor
construct(&first+i, data.first+i);
should be
construct(first+i, data.first+i);
EDIT
This from the initializer list constructor works.
construct(first+i, list.begin()[i]);
Now think carefully about the types here. The first argument is simple enough, it's a pointer to myString i.e. myString*. The second is a little more complicated because you have to understand std::initializer_list but if you follow it through it's a const reference to myString, i.e. const myString&. Now this is fine because construct is defined like this
template <class T>
void construct(T* first, const T& second);
i.e. for some type T it's first argument is a pointer to that type, and it's second argument is a const reference to that type.
Now look at the types for the second use of construct (the one that doesn't compile)
construct(first+i, data.first+i);
The first argument is myString* and the second argument is const myString*. Two pointers! This is why it doesn't compile! Your solution was to add an extra pointer to the first argument. The types of
construct(&first+i, data.first+i);
are myString** and const myString*. That compiles because you have a double pointer on the left and a single pointer on the right but it means that you are constructing pointers to myString not myString objects, and that is not what you want.
What you should have done is remove the pointer from the second argument, not add an extra pointer to the first argument. In other words the correct code is this
construct(first+i, data.first[i]);
Now the types are myString* for the first and const myString& for the second argument. Exactly the same as in the other constructor.
Try that change and let me know how it goes.
Related
Previous SO post consulted: Default Constructor Calls
Here is the code:
#include <iostream>
#include <unordered_map>
using namespace std;
struct item {
std::string name;
int price;
// item() = default;
item(std::string n, int p): name(n), price(p) {}
};
void createItems(std::unordered_map<std::string, item>& blah);
int main()
{
std::unordered_map<std::string, item> blah;
createItems(blah);
cout << blah["armor"].name << " " << blah["armor"].price << std::endl;
return 0;
}
void createItems(std::unordered_map<std::string, item>& blah) {
item i1{"armor", 1000};
item i2{"amulet", 200};
blah["armor"] = i1;
blah["amulet"] = i2;
return;
}
The error message when run:
main.cpp:20:25: required from here
/usr/include/c++/11/tuple:1824:9: error: no matching function for call to ‘item::item()’
1824 | second(std::forward<_Args2>(std::get<_Indexes2>(__tuple2))...)
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
main.cpp:10:5: note: candidate: ‘item::item(std::string, int)’
10 | item(std::string n, int p): name(n), price(p) {}
| ^~~~
main.cpp:10:5: note: candidate expects 2 arguments, 0 provided
main.cpp:6:8: note: candidate: ‘item::item(const item&)’
6 | struct item {
| ^~~~
main.cpp:6:8: note: candidate expects 1 argument, 0 provided
main.cpp:6:8: note: candidate: ‘item::item(item&&)’
main.cpp:6:8: note: candidate expects 1 argument, 0 provided
From my understanding, the default constructor is called but since I commented the line out the call results in an error.
Question: Why (and where) is the default constructor needed? My suspicion is that blah["armor"] calls the default constructor, but why?
As tkausl hinted at in their comment, the index operator is what needs the default constructor.
What the index operator does is
find tree location for key
if this location does not exist
create location
copy-construct key there
default-construct value there
return reference to value at location
Step 5 needs the default constructor. And since everything needs to be resolved at compile time, it needs the default constructor even if you never actually use a non-existent key. (You do, though.)
If you don't have a default constructor, you can't use the index operator. Use insert, emplace or try_emplace add elements to the map, and find or at to look them up.
I am studying the code of an open source app. I have made a simpler version of this code to isolate something bugging me (though I have several questions with this code that I am hoping C++ gurus will be able to help me with, I will start with the main one).
Main question: why do I need an "empty" constructor (no arguments) for the class of an object that's being assigned to a std::map?
The main idea (see code below) is to assign an instance of the Variant class to a std::map (whose key is a std::string). Here is the code:
#include <iostream>
#include <map>
#include <string>
#include <memory>
struct Data
{
public:
Data(const void* data, size_t bytes) : bytes(bytes)
{
ptr = malloc(bytes);
memcpy(ptr, data, bytes);
std::cout << "in ctor of Data" << std::endl;
}
~Data() { free(ptr); std::cout << "in dtor of Data" << std::endl; }
void* ptr{ nullptr };
size_t bytes;
};
struct DataStream
{
public:
DataStream(const std::shared_ptr<Data>& ptr, size_t size) : ptr(ptr), size(size)
{ std::cout << "in ctor of DataStream" << std::endl; }
std::shared_ptr<Data> ptr;
size_t size;
};
struct Variant
{
public:
enum Type
{
EMPTY,
TYPE1 = 5,
TYPE2 = 10
};
~Variant() { std::cout << "in dtor of Variant" << std::endl; }
// XXX If this ctor does NOT exist, the code doesn't compile XXX
Variant() : type(EMPTY) { std::cout << "in ctor of Variant" << std::endl; }
Variant(const int& n, Type type) : n(n), type(type) {}
Variant(const std::shared_ptr<Data>& data, Type type, size_t size) : type(type), data(std::make_shared<DataStream>(data, size))
{ std::cout << "in ctor of Variant (ptr to typed array)" << std::endl; }
Type type;
int n;
std::shared_ptr<DataStream> data;
};
struct Params
{
public:
void add(const std::string& name, const Variant& data) { params[name] = data; }
const Variant& operator[] (const std::string& name) { return params[name]; }
std::map<std::string, Variant> params;
};
struct Handle
{
public:
Params params;
void set(const std::string& name, const Variant& data) { params.add(name, data); }
};
int main()
{
Handle* handle = new Handle();
char data_i[3] = { 'a', 'b', 'c' };
std::shared_ptr<Data> data = std::make_shared<Data>(data_i, 3);
handle->set("testC", Variant(data, Variant::TYPE1, 3));
std::cout << "use_count data " << handle->params["testC"].data->ptr.use_count() << std::endl;
std::cout << "Variant type " << handle->params["testC"].type << std::endl;
delete handle;
return 0;
}
If I don't add to the class a constructor that doesn't take any arguments (what I call an empty constructor) the code doesn't compile. I get the following error msg:
test3.cpp:52:68: note: in instantiation of member function 'std::map<std::basic_string<char>, Variant>::operator[]' requested here
void add(const std::string& name, const Variant& data) { params[name] = data; }
^
test3.cpp:29:8: note: candidate constructor (the implicit copy constructor) not viable: requires 1 argument, but 0 were provided
struct Variant
^
test3.cpp:40:5: note: candidate constructor not viable: requires 2 arguments, but 0 were provided
Variant(const int& n, Type type) : n(n), type(type) {}
^
test3.cpp:41:5: note: candidate constructor not viable: requires 3 arguments, but 0 were provided
Variant(const std::shared_ptr<Data>& data, Type type, size_t size) : type(type), data(std::make_shared<DataStream>(data, size))
^
My understanding of what's going on is limited here. I do get that I am assigning an instance of the class Variant to a map and that an instance has to be created at this point of the code. Therefore the ctor of the object's class will be called. Makes sense. And since it has no argument it needs to use a constructor that does not take any argument. Fair enough. So I add the line for the constructor with no argument. The code compiles. Then, I output the type of the object stored in the map and I can see that even though the constructor with no argument was called for the creation of that object, it still has the type of the original object (5 rather than 0).
So this is the bit for which I'd like to have an explanation. How can it actually copy the content of the original object (the one that's created in main()) even though the constructor with no argument when assigning a copy of that object to std::map was used?
Subsidiary question #1 ):
If I look at the sequence of constructor/destructor for that Variant object I get the following:
in ctor of Data
in ctor of DataStream
in ctor of Variant (ptr to typed array)
in ctor of Variant
in dtor of Variant
use_count data 2
Variant type 5
in dtor of Variant
in dtor of Data
In the original code, the add method is:
void add(const std::string& name, Variant data) { params[name] = data; }
The const ref is not even there. And the sequence is:
in ctor of Data
in ctor of DataStream
in ctor of Variant (ptr to typed array)
in ctor of Variant
in dtor of Variant
in dtor of Variant
use_count data 2
Variant type 5
in dtor of Variant
in dtor of Data
The destructor of Variant is called 3 times but the constructors only twice! Not sure which constructor I am missing in this case.
But anyway, my question is: from the moment I create the temporary variable here:
handle->set("testC", Variant(data, Variant::TYPE1, 3));
Can I somehow insure that no copies of that object are made until I assign it to map? I have tried to add a bunch of std::move there & there, but it doesn't seem to make a difference. I am just thinking that these copies are not necessarily mandatory and that there must be a way of avoiding them. Your guru's input would be greatly appreciated for these 2 questions.
operator[] requires that the type is DefaultConstructible, if the key does not exist.
On line params[name] = data;
operator[] creates an element using the default constructor
operator[] returns a reference to the element
data is assigned to the reference, using the copy constructor
In your case, the step 1 fails because there is no default constructor.
C++17 adds insert_or_assign(), which does not require the type to be DefaultConstructible.
Here's the code I have:
#include <iostream>
#include <functional>
struct Test {
struct Envelope {
const int x = 1;
int y = 2;
int z = 3;
};
Envelope mEnvelope;
struct Buffer {
Envelope mEnvelope;
} mBuffer;
std::function<Buffer()> func{[this] {
mBuffer.mEnvelope = mEnvelope;
return mBuffer;
}};
};
int main() {
Test test;
}
it says:
g++ -std=c++17 -Wall -pedantic -pthread main.cpp && ./a.out
main.cpp: In lambda function:
main.cpp:17:29: error: use of deleted function 'Test::Envelope& Test::Envelope::operator=(const Test::Envelope&)'
17 | mBuffer.mEnvelope = mEnvelope;
| ^~~~~~~~~
main.cpp:5:12: note: 'Test::Envelope& Test::Envelope::operator=(const Test::Envelope&)' is implicitly deleted because the default definition would be ill-formed:
5 | struct Envelope {
| ^~~~~~~~
main.cpp:5:12: error: non-static const member 'const int Test::Envelope::x', can't use default assignment operator
I've tried using a Copy Constructor:
Envelope(const Envelope &other) {
y = other.y;
}
or override the operator =
Envelope &operator=(Envelope &other) {
// self-assignment guard
if (this == &other) {
return *this;
}
y = other.y;
}
But the errors grown even more.
I need to copy only some "part" of the object.
This is just a test, of course the real object have lots of members fields, and some need to be ignored.
How to do it within a std::function<Buffer()>?
OK, since it might not be obvious what the problem is:
A copy assignment operator should usually have a const& parameter, not a & parameter.
You should then also provide the copy constructor of Envelope as you showed. First of all it has different behavior than the implicitly-generated one (which would copy over all the elements, not only y) and the generation of the implicit copy constructor when there is a user-defined copy assignment has been deprecated since C++11 and will probably be removed in a future standard iteration.
Then you will need to default the default constructor as well, since you have a user-defined constructor now:
Envelope() = default;
Furthermore, your copy assignment operator is declared to return a Envelope& (as it should), but you forgot to actually put a return statement in it at its end, so executing it will cause undefined behavior as is:
return *this;
You can create your own copy ctor/operator in order to only copy the info you want:
#include <iostream>
#include <functional>
struct Test {
typedef struct {
const int x;
int y;
int z;
} Envelope_t;
public:
Test():env({1,2,3}){}
Test(const Test & copy):env({copy.env.x,5,copy.env.z}) {}
Test& operator=(const Test& copy){
env.y=copy.env.y+7;
env.z=copy.env.z;
return *this;
}
void printValues() {
std::cout << "x:" << env.x << "\ny:" <<
env.y << "\nz:" << env.z << "\n\n";
}
private:
Envelope_t env;
};
int main() {
Test original;
original.printValues();
Test copyCtor(original);
copyCtor.printValues();
Test copyOp;
copyOp = copyCtor;
copyOp.printValues();
}
#include <iostream>
using namespace std;
class StringNum {
public:
string s;
StringNum() {s = "";}
public:
StringNum(int n) {
for (int i=1; i<=n; i++) s += "x";
}
operator int () {
return s.length();
}
StringNum operator - (StringNum v) {
int len = s.length() - v.s.length();
StringNum res;
for (int i=1;i<=len;i++) res.s += "x";
return res;
}
/* // long solution. But this will allow the program to run.
template <class T>
StringNum operator - (T value) {
return (*this) - StringNum(value);
}
*/
};
int main()
{
StringNum x(4);
cout << 3 - x; // this compiles
cout << x - 3; // error: ambiguous overload for operator -
// change the program so that the 2nd line output 2
return 0;
}
So I have a class that can be upcast from int/downcast to int (this is the simplified version, in the actual version StringNum is HighPrecisionFloat, and int is int/float/double/.. . etc).
When I compile the program, the error message
In function 'int main()':|
error: ambiguous overload for 'operator-' (operand types are 'StringNum' and 'int')|
note: candidate: operator-(int, int) <built-in>|
note: candidate: StringNum StringNum::operator-(StringNum)|
This happens because there are 2 ways to understand x - 3 :
a) int(x) - 3
b) x - StringNum(3)
One way to do this is to use template for each operator (+, -, *, /, dot product, etc...) But that is not very convenient because I have to write templates for each operator.
Is there a better solution to this problem? I wish to call x - StringNum(3). Thank you.
You can make your constructor and conversion to int explicit.
The compiler won't make implicit conversion between those types anymore, but you can still use them like so.
auto stringNum = StringNum{3}; //int to StringNum
int y = static_cast<int>(stringNum);
I'm simply adding two numeric arrays of size 3 and printing the result. So, if:
A = 4,6,1
B = 8,5,6
Then the result is 12,11,7. The problem for me is I am printing exponential numbers i.e. -1.28823e-231. Can anyone tell me why? I have tried to keep as little code in here as possible. First it's main() then the header then the source material. Many thanks.
NumericArray<double> doubArray5; //test + operator
cout << "doubArray5" << endl;
doubArray5 = doubArray2 + doubArray3;
for (int i = 0; i < doubArray5.Size(); i++)
cout << doubArray5[i] << endl;
cout << endl;
#ifndef NUMERICARRAY_H
#define NUMERICARRAY_H
#include "array.h"
#include <iostream>
namespace Cary
{
namespace Containers
{
template<typename T>
class NumericArray: public Array<T>
{
public:
NumericArray<T>(); //default constructor
NumericArray<T>(int i); //constructor with one argument
~NumericArray<T>(); //destructor
NumericArray<T>(const NumericArray<T>& source); //copy constructor
NumericArray<T>& operator = (const NumericArray<T>& arr1); //assignment operator
NumericArray<T> operator * (double factor) const; //scale
NumericArray<T>& operator + (const NumericArray<T>& arr2) const; //add
T dotProduct(const NumericArray<T>& na) const; //dot product
};
}
}
#ifndef NUMERICARRAY_CPP
#include "NumericArray.cpp"
#endif
#endif
template<typename T>
NumericArray<T>& NumericArray<T>::operator + (const NumericArray<T>& arr) const //add
{
if (Array<T>::Size() != arr.Size()) throw OutOfBoundsException();
NumericArray<T> tempArray = NumericArray(Array<T>::Size());
for (int i = 0; i < Array<T>::Size(); i++)
tempArray[i] = Array<T>::GetElement(i) + arr.GetElement(i);
return tempArray;
}
Idiomatically (i.e. based on the "behaves like int" guideline when overloading numeric operators), operator+() usually returns by value, not by reference, since the result of addition is a distinct value (or object) from either of those being added.
Specifically, as Mike Seymour also mentioned in comments, your operator+() is returning a reference to a local variable that ceases to exist when operator+() returns. That causes the caller to exhibit undefined behaviour if it subsequently attempts to use the returned reference.
You are returning a reference to a local variable (tempArray in operator +).
When the function returns, tempArray is destroyed. The caller then tries to use the reference to a now-destroyed object, and reads garbage.