Syntax on Constructors with Const Members [duplicate] - c++

This question already has answers here:
How to initialize a const field in constructor?
(6 answers)
How to initialize const member variable in a class?
(11 answers)
Closed 9 years ago.
I haven't done C++ coding in some time, and my friend is having trouble with his homework. I never really worked with const, and it's making this a nightmare as I can't figure out the correct syntax for the constructor. Imagine I have this in dvd.h:
class DVD {
const string title;
int minutes;
double price;
public:
DVD(const string t, int m, double p);
}
3 private member variables, the string is const. The constructor also takes a const string.
So now, in dvd.cpp I can do the following:
#include "dvd.h"
DVD::DVD(const string t, int m, double p) {
const string title = t;
minutes = m;
price = p;
}
And all is well in the world. However, when I modify minutes in dvd.h to be const (which is how his professor structured the file), we have this in dvd.h:
class DVD {
const string title;
const int minutes; // Here is the change
double price;
public:
DVD(const string t, int m, double p);
}
So, now that minutes is const, I get the following compilation errors:
assignment of read-only member 'DVD::minutes' dvd.cpp
uninitialized member 'DVD::minutes' with 'const' type 'const int' [-fpermissive] dvd.cpp
Which I suppose makes sense, because I'm trying to set a value into a const variable. So then I tried doing the same thing as with the const string title in dvd.cpp in order to resolve the error:
DVD::DVD(const string t, int m, double p) {
const string title = t;
const int minutes = m; // Was 'minutes = m;'
price = p;
}
and got the following (1) errors and (1) warnings:
uninitialized member 'DVD::minutes' with 'const' type 'const int' [-fpermissive] dvd.cpp
unused variable 'minutes' [-Wunused-variable] dvd.cpp
So I guess I'm struggling to figure out what the darn syntax is for this... title and minutes are supposed to be const, but the constructor's parameter list for DVD takes only a const string. I can't figure out what I'm missing - it's been a while since I last coded in C++.

const string title = t;
That declares a local variable. All is not well with the world: you haven't set the member variable to the value you want. To initialise members, use the constructor's initialiser list:
DVD::DVD(const string t, int m, double p) :
title(t), minutes(m), price(p)
{}
Your version tries to default-initialise each member (since they aren't mentioned in the initialiser list), then assign each of them. This doesn't work for members which can't be default-initialised (such as references, or class types without a default constructor) or assigned to (such as const members).

You can initialize your const and non-const members simply like this:
DVD::DVD(const string t, int m, double p)
: title(t)
, minutes(m)
, price(p)
{}
Note that, if you are using C++98/03, you may want to pass the string parameter as const& (to avoid useless and inefficient calls to string copy constructor):
DVD(const string& t, ...other stuff...)
... same init as above
If you are using C++11 (which implements move semantics), you can pass the string parameter by value, and std::move() from the value:
DVD::DVD(string t, int m, double p)
: title(std::move(t)),
, minutes(m)
, price(p)
{}

Related

Internally sorting a class that inherits from a vector of pointers to a user defined object (C++)

So I defined a class on C++ that inherits from a vector of pointers:
class SuperBinList : public std::vector<SuperBin*>{
public:
SuperBinList();
SuperBinList(const std::vector<SuperBin*>& superBinList);
virtual ~SuperBinList();
SuperBinList& operator += (SuperBin* superBin);
SuperBinList& operator += (const SuperBinList& superBin);
void sortByZbi(const double sys);
void sortBySoverB() const;
};
The SuperBin class itself is defined as:
class SuperBin{
public:
SuperBin(const VI index, const double nSig, const double nBkg, const VS mPerpLabel, const VS rIsrLabel, const VS visLabel);
virtual ~SuperBin();
VI getIndex();
double getNsig();
double getNbkg();
double getSoverB();
double getBinZbi(const double sys);
VS getMperpLabel();
VS getRisrLabel();
VS getVisLabel();
SuperBin* tryMerge(SuperBin* superBin, double sys);
private:
VI index_;
double nSig_;
double nBkg_;
double sOverB_;
VS mPerpLabel_;
VS rIsrLabel_;
VS visLabel_;
};
Now the problem I'm having is that I want the SuperBinList class to be able to sort itself in descending order in terms of any of the (double type) members of the SuperBin class (such as sOverB). For this I tried the following method using a lambda function:
void SuperBinList::sortBySoverB() const{
std::sort(this->begin(), this->end(), [](const SuperBin* lhs, const SuperBin* rhs){
return lhs->getSoverB() < rhs->getSoverB();});
}
The top error I'm getting is:
error: passing 'const SuperBin' as 'this' argument discards qualifiers [-fpermissive]
return lhs->getSoverB() < rhs->getSoverB();});
Which, as I understood from similar threads, has to do with the const specifiers. However, I'm still not sure what it is that I am doing wrong. Any help would be greatly appreciated. Please pardon my ignorance as I am a physics PhD and not a computer scientist.
You have your SuperBinList::sortBySoverB() function defined as const, which means it is not allowed to modify SuperBinList, and
this will have the type const SuperBinList *, rather than SuperBinList *
Similarly, your lambda is define with const pointers const SuperBin* lhs, meaning you can only call const functions.
Change your function definitions to void SuperBinList::sortBySoverB() {} and double getSoverB() const {}, and it should compile. (Generally member functions should be marked const if they are read-only operations like getters).

How to define an implicit conversion for typedefs?

I want to have an automatic conversion from std::string into my type my_type, defined as
typedef std::pair<std::string,int> my_type;
such that the .first part of the converted my_type is the string and the .second part is always 0.
It should also work if one calls the function std::string fun(my_type x, ...) { return x.first; } with, say,
std::string s = "Hello"; fun(s, ...);.
I don't want to define a new class instead of my_type and also not to overload all of my functions if possible. I tried to wrap my head around how to use operator for this but I can't get my program to compile.
EDIT:
Since this doesn't seem to be possible without defining a custom struct or so, here is a workaround that I came up with, but I was hoping it can be achieved without defining a new class/struct. Thank you for saving me more time trying to do this, though.
class Element {
public:
Element() {};
Element(std::string s, int a) { name = s; value = a; };
Element(std::string s) { name = s; value = 0; };
...
std::string return_name() { return name; };
private:
std::string name;
int value;
};
std::string fun(Element x) { return x.return_name(); };
Calling std::string s = "Hello"; fun(s); works now automatically.
It is not possible to add new implicit conversions for existing classes, such as std::pair. Implicit conversions can only be member functions:
A non-explicit constructor that can be called with one argument. If there are more arguments, they must have default values.
operator T() const conversion operator.
And it is not possible to add new member functions to classes without changing class definition. This restriction is in place to prevent a function introduced at global or namespace scope from changing the semantics of your existing code.
What you can do instead is create a new class with a conversion constructor (a non-explicit constructor that can be called with one argument):
struct MyPair : std::pair<std::string, int> {
// In this class scope pair now refers to std::pair<std::string, int>.
MyPair(std::string const& a)
: pair(a, 0)
{}
MyPair(pair const& a)
: pair(a)
{}
};
The derivation from std::pair<std::string, int> makes it possible to pass MyPair where std::pair<std::string, int> is expected. And another constructor for converting std::pair<std::string, int> to MyPair.

undeclared identifier using class compared to main function

I want to understand why I receive a syntax error for the same syntax in different code areas.
For example:
#include<iostream>
class Grading
{
public:
Grading();
~Grading();
private:
//Here syntax is broken
//reason: undeclared Identifier
const int studentID = 50;
int students[studentID];
};
int main() {
//Here syntax is fine.
const int studentID = 50;
int students[studentID];
return 0;
}
const int studentID = 50; should be static const int studentID = 50;. Right now you are declaring studentID as a non-static class member and it will constructed (and assigned a value 50) only when class instance is constructed while to declare an array compiler requires array size to be known at compile time. Basically your code is equivalent to this:
class Grading
{
public:
Grading(): studentID(50) {}
~Grading();
private:
const int studentID;
int students[studentID];
};
If you write const int studentID = 50; outside of class scope (in main for example) then it would be just a regular constant with a value 50 known at compile time.
The size of a C++ member array must be a constexpr - known at compile time, simple const is not enough, since it will be initialized at runtime, when you create an instance of the class.
However, static const is enough, since you must initialize it with a constexpr, so the value is known at compile time.

Initializing struct via member initialization list

So I'm learning C++ from Stephen Prata book and I want to do one exercise... So the problem is this:
I want to use a std::valarray inside a struct, inside a class like this:
class Wine
{
private:
struct Pair
{
std::valarray<int> valYear;
std::valarray<int> valBottlesNum;
};
int m_yearNum;
Pair m_numericData;
public:
Wine();
Wine(int, const int[], const int[]);
};
And initialize this via member initialization list:
Wine::Wine(int yearNum, const int year[], const int bottlesNum[])
: m_yearNum(yearNum),
m_numericData.valYear(yearNum, year),
m_numericData.valBottlesNum(yearNum, bottlesNum)
{}
But it just doesn't want to work. Somehow compiler does not like this "." to access members of a m_numericData struct in a initializer list.
I could just abandon Pair struct and do valYear and valBottlesNum as a simple class member variables and initilize them like this...
Wine::Wine(, int yearNum, const int year[], const int bottlesNum[])
: m_yearNum(yearNum), m_valYear(yearNum, year), m_valBottlesNum(yearNum, bottlesNum)
{}
but I really want to know how to solve this kinda stuff.
Thanks in adavnce!
You can move the individual initialisations into the body of the constructor:
Wine::Wine(int yearNum, const int year[], const int bottlesNum[])
: m_yearNum(yearNum)
{
m_numericData.valYear = std::valarray<int>(yearNum, year);
m_numericData.valBottlesNum = std::valarray<int>(yearNum, bottlesNum);
}
Alternatively, give Pair its own constructor.
The valarray constructor you're attempting to use takes a T const* to the data and an std::size_t argument indicating the number of array elements that the first argument points to. If you can use C++11, then change yearNum to std::size_t and you can use list-initialization, which in turn will aggregate initialize the Pair.
Wine::Wine(std::size_t yearNum, const int year[], const int bottlesNum[])
: m_yearNum(yearNum)
, m_numericData{{year, yearNum}, {bottlesNum, yearNum}}
{}
Live demo

invalid use of non-static member function [duplicate]

This question already has answers here:
problem sorting using member function as comparator
(9 answers)
Closed 7 years ago.
I have something like this:
class Bar
{
public:
pair<string,string> one;
std::vector<string> cars;
Bar(string one, string two, string car);
};
class Car
{
public:
string rz;
Bar* owner;
Car(string car, Bar* p);
};
class Foo
{
public:
Foo ( void );
~Foo ( void );
int Count ( const string & one, const string & two) const;
int comparator (const Bar & first, const Bar & second) const;
std::vector<Bar> bars;
};
int Foo::comparator(const Bar & first, const Bar & second) const{
return first.name < second.name;
}
int Foo::Count ( const string & one, const string & two ) const{
int result=0;
Bar mybar = Bar( one, two, "" );
std::vector<Bar>::iterator ToFind = lower_bound(bars.begin(), bars.end(), mybar, comparator);
if (ToFind != bars.end() && ToFind->one == mybar.one ){
result = ...
}
return result;
}
The method Foo::Count should use std::lower_bound() to find element in vector<Bar> according to pair of two strings.
Now the part which doesn't work. To lower_bound() I'm providing method comparator(). I thought it was okay, but g++ says:
c.cpp: In member function ‘int Foo::Count(const string&, const string&) const’:
c.cpp:42:94: error: invalid use of non-static member function
std::vector<Bar>::iterator ToFind = lower_bound(bars.begin(), bars.end(), mybar, comparator);
And the method Count() must stay const...
I'm quite new to C++ because I'm forced to learn it.
Any ideas?
The simplest fix is to make the comparator function be static:
static int comparator (const Bar & first, const Bar & second);
^^^^^^
When invoking it in Count, its name will be Foo::comparator.
The way you have it now, it does not make sense to be a non-static member function because it does not use any member variables of Foo.
Another option is to make it a non-member function, especially if it makes sense that this comparator might be used by other code besides just Foo.
You must make Foo::comparator static or wrap it in a std::mem_fun class object. This is because lower_bounds() expects the comparer to be a class of object that has a call operator, like a function pointer or a functor object. Also, if you are using C++11 or later, you can also do as dwcanillas suggests and use a lambda function. C++11 also has std::bind too.
Examples:
// Binding:
std::lower_bounds(first, last, value, std::bind(&Foo::comparitor, this, _1, _2));
// Lambda:
std::lower_bounds(first, last, value, [](const Bar & first, const Bar & second) { return ...; });
You shall pass a this pointer to tell the function which object to work on because it relies on that as opposed to a static member function.