I am writing a class and I got to the point where I can do operations that mix my class type objects and C++ literals, but in one direction only.
here is a simplified code that shows the idea:
#include <iostream>
#include <string>
using namespace std;
class CLS
{
string str;
public:
CLS(const char* param)
{ str = param; }
CLS operator+(const CLS& rhs)
{
str = str + rhs.str;
return *this; }
friend ostream& operator<<(ostream& out, const CLS& rhs);
};
ostream& operator<<(ostream& out, const CLS& rhs)
{
out << rhs.str;
return out; }
int main()
{
CLS a("\n Hello ");
CLS b("bye!\n\n");
cout << a + "World!\n\n";
//cout << "\n Good " + b; /* this is not possible because of the operands order */
}
As you see, I can do something like:
a + "W";
but not,
"W" + a;
As indicated in the last line of the code.
I understand the reason.
The first is equivalent to:
a.operator+("W");
which is covered by my class. However, the second is like,
"W".operator(a);
which is not covered and the literal itself is not an object of a class as I understand. And so, the expression as whole cannot be.
I understand I can create a user defined literals, but this is not what I want to do here. (although I am not sure if they gonna work or not).
I could not find any hint browsing questions I supposed to be related on this site, and I could not get something related to my issue on the net.
My question:
Is there a way that can make either order works?
This code:
cout << "\n Good " + b; /* this is not possible because of the operands order */
does not work because you made operator+ member (and not const member). If you rewrite it as standalone function (probably friend) then this problem would go away:
friend
CLS operator+(const CLS& lhs, const CLS& rhs)
{
CLS r;
r.str = lhs.str + rhs.str;
return r;
}
if you create additional ctor that accepts const std::string & it would be even simpler:
friend
CLS operator+(const CLS& lhs, const CLS& rhs)
{
return CLS( lhs.str + rhs.str );
}
note, you should rewrite existing constructor this way:
CLS(const char* param) : str( param )
{}
it is cleaner and more efficient way
You can add a global function:
inline CLS operator+(const char *lhs, const CLS& rhs)
{
return CLS(lhs) + rhs;
}
The assert in main.cpp is failing, and I don't understand why.
Here is string.hpp
class String
{
private:
int len;
char* str;
public:
String(char const* s); // C-string constructor
~String() {delete str;}; // destructor
char* const getString(); //get string for printing
};
inline bool operator==(String lhs, String rhs)
{
return std::strcmp(lhs.getString(),rhs.getString());
}
// Define operator!= in terms of ==
inline bool operator!=(String const& lhs, String const& rhs)
{
return !(lhs == rhs);
}
and here is string.cpp
String::String(char const* s) // C-string constructor
{
len = std::strlen(s);
str = new char[len+1];
std::strcpy(str,s);
}
char* const String::getString()
{
return str;
}
and here is main.cpp
#include <cassert>
int main()
{
String c = "c";
String d = "d";
assert(c == c);
assert(c != d);
}
I tried to include only the essential code. I left out a lot of the obvious includes. The assert(c == d) is failing and I don't understand why. The operator overload of == should have returned a true result.
std::strcmp returns 0 if the strings are equal. So your operator== will return false for equal strings and true else.
You could, for instance, switch the implementations of == and != around,
strcmp returns 0 when its arguments have equal contents.
So add a comparison with 0 to your operator==:
inline bool operator==(String const& lhs, String const& rhs)
{
return std::strcmp(lhs.getString(), rhs.getString()) == 0;
}
Also, since you probably don't want to copy the arguments each time you call operator==, I'd recommend passing them by reference.
I'm learning C++ and I've been writing a wrapper for std::map and std::string, and I've stumbled upon a problem. Whenever I add something to the map using a string as key, once I try to access that item using the exact same key it says the key is out of bounds of the map. Here's my code (irrelevant parts left out):
ADictionary.h
#ifndef ADICTIONARY_H
#define ADICTIONARY_H
#include <map>
...
template<typename KEY, typename VALUE>
class ADictionary {
public:
...
VALUE operator [](KEY key) const {
return value.at(key);
}
void add(KEY key, VALUE value) {
this->value.insert(std::make_pair(key, value));
}
...
private:
std::map<KEY, VALUE> value;
};
#endif
AString.cpp
#include "AString.h"
AString::AString() {
value = "";
}
AString::AString(const char character) {
value = character;
}
AString::AString(const char * characters) {
value = characters;
}
AString::AString(std::string text) {
value = text;
}
...
AString::operator const char *() const {
return value.c_str();
}
AString::operator const std::string() const {
return value;
}
...
ABoolean AString::operator<(AString & text) const {
return getLength() < text.getLength();
}
ABoolean AString::operator>(AString & text) const {
return text < *this;
}
ABoolean AString::operator==(AString & text) const {
return value == text.value;
}
ABoolean AString::operator!=(AString & text) const {
return !(text == *this);
}
AString & AString::operator=(AString & text) {
value = text.value;
return *this;
}
...
The code which uses the above
ADictionary<AString, AString> test;
AString a = "a";
AString b = "b";
test.add(a, b);
std::cout << test[a]; // Error occurs here, according to the program "a" is not a key in the map
I hope someone can explain to me what's going wrong. I've tried creating a dictionary with the default std::string as types and it worked correctly:
ADictionary<std::string, std::string> test;
std::string a = "a";
std::string b = "b";
test.add(a, b);
std::cout << test[a]; // No error this time
As I've said, I'm pretty new to C++ so there may be other errors. If so, feel free to point them out.
Thanks!
EDIT:
AString.h
#ifndef ASTRING_H
#define ASTRING_H
#include <string>
#include "ABoolean.h"
#include "AInteger.h"
#include "AList.h"
class ABoolean;
class AInteger;
template<typename VALUE>
class AList;
class AString {
public:
AString();
AString(const char);
AString(const char *);
AString(std::string);
~AString();
operator const char *() const;
operator const std::string() const;
operator const AInteger() const;
ABoolean operator<(AString &) const;
ABoolean operator>(AString &) const;
ABoolean operator==(AString &) const;
ABoolean operator!=(AString &) const;
AString & operator=(AString &);
AString & operator+(AString &);
AString & operator+=(AString &);
void clear();
ABoolean contains(AString) const;
AInteger getIndex(AString) const;
AInteger getLength() const;
AList<AString> getSplit(AString) const;
AString getSubstring(AInteger, AInteger) const;
void removeRange(AInteger, AInteger);
void removeSubstring(AString);
void toLowercase();
void toUppercase();
private:
std::string value;
};
AString & operator+(const char, AString &);
AString & operator+(const char *, AString &);
#endif
Your string operators appear to be incorrect.
std::map uses the less than operator by default. While you provide one for AString, the only thing it does is check the length of the string. What if the two strings are of equal length?
The correct thing to do is to lexicographically compare the characters in the string. While there is a standard library function to do this, you can use operator < of the std::string values in your class:
friend bool operator<(AString const& a, AString const& b)
{
return a.value < b.value;
}
EDIT: You may also wish to remove your conversion operators, or at least make them explicit, which prevents surprising and unwanted implicit conversions. Constructors taking one parameter (other than copy or move constructors) should also be declared explicit.
Consider this example
(Note that this is just something I made up to illustrate the problem. I am well aware there are more efficient ways to parse an arithmetic expression and though the subject is fascinating, this has nothing to do with my actual question. It's just a semi-realistic example, if I might say so.
I agree the parser thing might make the question seem more complicated, but I could not think of a more abstract example).
Assume you want to do a simple expression parser. You will get bits of strings from a tokenizer, some of them being possibly ambiguous.
For instance, the string "-" could represent an unary minus or a binary minus.
Assume you want to get all possible meanings for string "-".
You could do as follows:
1) define a sorted array describing all possible operators
// types of operators
enum class opType: char { unary, lasso, rasso, none };
// operator descriptors
struct opDesc {
string symbol;
opType type;
char priority;
// partial order comparison
bool operator< (const opDesc& a) const
{
// unary operators first
if (symbol == a.symbol) return type < a.type;
return symbol < a.symbol;
}
// comparison with strings
static bool comp_desc_str (const opDesc& a, const string& s)
{
return a.symbol < s;
}
static bool comp_str_desc (const string& s, const opDesc& a)
{
return s < a.symbol;
}
};
static opDesc op_descriptors[] = {
{ "+" , opType::unary, 8 }, // unary +
{ "-" , opType::unary, 8 }, // unary -
{ "*" , opType::lasso, 6 }, // multiplication
{ "/" , opType::lasso, 6 }, // division
{ "+" , opType::lasso, 5 }, // addition
{ "-" , opType::lasso, 5 }, // substraction
};
2) use std::equal_range to get all possible matches for a given string
// sort descriptors by value and type
sort(begin(op_descriptors), end(op_descriptors));
// do some searches
string patterns[] = { "+", "-", ">>", "**" };
for (string s : patterns)
{
pair<opDesc*, opDesc*> ops;
ops = equal_range(
std::begin(op_descriptors),
std::end (op_descriptors),
s,
opDesc::comp_desc_str);
cout << s <<": "<< ops.first[0] << ops.second[-1] << endl;
}
This code won't compile, complaining about opDesc::comp_desc_str (it expects parameters the other way around, i.e. string first, opDesc next).
If I try to replace the function with a version that takes its arguments in reverse order:
ops = equal_range(
std::begin(op_descriptors),
std::end (op_descriptors),
s,
opDesc::comp_str_desc);
it won't compile either, complaining about parameters being yet again in the wrong order (at some other point of the algorithm).
This code, however, will work (see a live version here)
#include <regex>
#include <iostream>
using namespace std;
// types of operators
enum class opType: char { unary, lasso, rasso, none };
// operator descriptors
struct opDesc {
string symbol;
opType type;
char priority;
// partial order comparison
bool operator< (const opDesc& a) const
{
// unary operators first
if (symbol == a.symbol) return type < a.type;
return symbol < a.symbol;
}
// comparison with strings
static bool comp_desc_str (const opDesc& a, const string& s)
{
return a.symbol < s;
}
static bool comp_str_desc (const string& s, const opDesc& a)
{
return s < a.symbol;
}
// display
friend ostream& operator<<(ostream& os, const opDesc& op);
};
ostream& operator<<(ostream& os, const opDesc& op)
{
os << op.symbol << "[" << (int)op.type << ":" << (int)op.priority << "]";
return os;
}
static opDesc op_descriptors[] = {
{ "+" , opType::unary, 8 }, // unary +
{ "-" , opType::unary, 8 }, // unary -
{ "~" , opType::unary, 8 }, // bitwise not
{ "**", opType::rasso, 7 }, // power
{ "*" , opType::lasso, 6 }, // multiplication
{ "/" , opType::lasso, 6 }, // division
{ "%" , opType::lasso, 6 }, // remainder
{ "+" , opType::lasso, 5 }, // addition
{ "-" , opType::lasso, 5 }, // substraction
{ "<<", opType::lasso, 4 }, // left shift
{ ">>", opType::lasso, 4 }, // right shift
{ "&" , opType::lasso, 3 }, // bitwise and
{ "^" , opType::lasso, 2 }, // bitwise xor
{ "|" , opType::lasso, 1 }, // bitwise or
{ "(" , opType::none , 0 }, // braces
{ ")" , opType::none , 0 }
};
int main(void)
{
// sort descriptors by value and type
sort(begin(op_descriptors), end(op_descriptors));
// do some searches
string patterns[] = { "+", "-", ">>", "**" };
for (string s : patterns)
{
pair<opDesc*, opDesc*> ops;
// this won't work
/*
ops = equal_range(
std::begin(op_descriptors),
std::end (op_descriptors),
s,
opDesc::comp_desc_str or opDesc::comp_str_desc);
*/
// this works
ops.first = lower_bound(
std::begin(op_descriptors),
std::end (op_descriptors),
s, opDesc::comp_desc_str);
ops.second = upper_bound(
std::begin(op_descriptors),
std::end (op_descriptors),
s, opDesc::comp_str_desc);
cout << s <<": "<< ops.first[0] << ops.second[-1] << endl;
}
}
output:
+: +[0:8]+[1:5] // unary and binary "-" operators found
-: -[0:8]-[1:5] // same thing for "+"
>>: >>[1:4]>>[1:4] // first == second when there is only
**: **[2:7]**[2:7] // one version of the operator
I tried this code on VisualC++ 2013 and g++ with the same results
(only the obfuscation of the template error messages vary).
Questions
is there a particular reason why lower_bound and upper_bound should require two different custom comparison functions?
is there a workaround for MSVC throwing a bogus error in debug build when using functors to work around the problem?
is there a definite workaround for this problem (i.e. using equal_range as intended instead of doing the job twice and make it compile in debug mode on Visual C++ 2013)?
std::lower_bound requires comp(*it, val) whereas std::upper_bound requires comp(val, *it).
So your comp functor have to provide both bool operator () (const opDesc& a, const string& s) const and bool operator ()(const string& s, const opDesc& a) const.
So, you may use following comp functor:
struct lessOpDescWithString
{
bool operator () (const opDesc& lhs, const std::string& rhs) const {
return opDesc::comp_desc_str(lhs, rhs);
}
bool operator () (const std::string& lhs, const opDesc& rhs) const {
return opDesc::comp_str_desc(lhs, rhs);
}
};
My own answer, only summarizing other contributions, notably Jarod42's:
Why the two comparisons
The algorithm of equal_range requires both > and < comparisons between internal (opDesc in this example) and foreign (std::string) types.
You can't deduce a<b from !(b<a), because of the == case, so you have to provide two different comparators.
Functionnally, you could pick any working combination of comparison operations, for instance < and > or < and <=, but the std:: guys settled for a fixed < comparison with arguments swapped around, which is a choice dictated by the signature of the function: it only has to define a (type, foreign type) and a (foreign type, type) variant.
lower_bound only requires < (expressed as type < foreigh type) while upper_bound only requires > (expressed as foreign type < type), so both can work with a single function, but equal_range must have access to both prototypes.
The way to do it
The practical solution is to define a function object aka functor to do the job:
// operator descriptors
struct opDesc {
string symbol;
opType type;
char priority;
// partial order comparison
bool operator< (const opDesc& a) const
{
// unary operators first
if (symbol == a.symbol) return type < a.type;
return symbol < a.symbol;
}
// functor to compare with strings
struct comp
{
bool operator() (const opDesc& a, const std::string& b) const
{
return a.symbol < b;
}
bool operator() (const std::string& a, const opDesc& b) const
{
return a < b.symbol;
}
};
and use it like so:
pair<opDesc*, opDesc*> ops;
ops = equal_range(
std::begin(op_descriptors),
std::end (op_descriptors),
s,
opDesc::comp()); // <- functor to provide two different comparison functions
MSVC bug
Besides, this won't compile on MSVC++ 2013 due to an obscure paranoid check enabled only in debug mode. The release version will compile fine, as will the code in g++ regardless of debug level.
Judging from the cryptic names used, it seems the template checks whether the comparison defines a total order (which it shouldn't since the whole point of this API is to work on partially ordered structures).
My current (ugly) workaround is to disable some internal debug flag:
#if (defined _MSC_VER && defined _DEBUG)
#define _ITERATOR_DEBUG_LEVEL 1
#endif // _MSC_VER && _DEBUG
prior to including std:: headers
Another possible workaround suggested by Jarod42 is to define the missing comparison function.
// functor to compare with strings
struct comp
{
bool operator() (const opDesc& a, const std::string& b)
{ return a.symbol < b; }
bool operator() (const std::string& a, const opDesc& b)
{ return a < b.symbol; }
// just to make Microsoft Visual C++ happy when compiling in debug mode
bool operator() (const opDesc& a, const opDesc& b)
{ assert(false); return false; }
};
I suppose that std::lower_bound and std::upper_bound will use a binary search on your sorted array and must be able to compare obDesc to std::string and vice versa. I would suggest making a comparator like
struct obDescStrCmp {
bool operator()(const opDesc& lhs, const opDesc& rhs) const {
// code to compare obDesc to opDesc
}
bool operator()(const opDesc& lhs, const std::string& rhs) const {
// code to compare obDesc to std::string
}
bool operator()(const std::string& lhs, const opDesc& rhs) const {
// code to compare std::string to opDesc
}
bool operator()(const std::string& lhs, const std::string& rhs) const {
// code to compare std::string to std::string
// I'm not sure if this is really necessary.
}
};
and pass it to your std algorithm of choice instead of relying on the operators defined in your opDesc struct. The compiler should select the correct overload based on the actual order of parameters in the implementations of the std algorithms.
Edit: Replace operator< with operator() to make the struct callable.
It's an exercise from C++ Primer 5th Edition:
Exercise 14.7: Define an output operator for you String class you
wrote for the exercises in ยง 13.5 (p. 531).(Page 558)
The string.h I wrote for previous exercises:
/**
* #brief std::string like class without template
*
* design:
*
* [0][1][2][3][unconstructed chars][unallocated memory]
* ^ ^ ^
* elements first_free cap
*/
class String
{
friend std::ostream& operator <<(std::ostream& os, const String& s);
public:
//! default constructor
String();
//! constructor taking C-style string i.e. a char array terminated with'\0'.
explicit String(const char * const c);
//! copy constructor
explicit String(const String& s);
//! move constructor --07.Jan.2014
String(String&& s) noexcept;
//! operator =
String& operator = (const String& rhs);
//! move operator = --07.Jan.2014
String& operator = (String&& rhs) noexcept;
//! destructor
~String();
//! members
char* begin() const { return elements; }
char* end() const { return first_free; }
std::size_t size() const {return first_free - elements; }
std::size_t capacity() const {return cap - elements; }
private:
//! data members
char* elements;
char* first_free;
char* cap;
std::allocator<char> alloc;
//! utillities for big 3
void free();
};
std::ostream&
operator << (std::ostream& os, const String& s);
Part of the string.cpp:
//! constructor taking C-style string i.e. a char array terminated with'\0'.
String::String(const char * const c)
{
auto p = c;
char* newData = alloc.allocate(sizeof(p));
std::uninitialized_copy(p, (p + sizeof(p)), newData);
//! build the data structure
elements = newData;
cap = first_free = newData + sizeof(c);
}
std::ostream &operator <<(std::ostream &os, const String &s)
{
std::for_each(&s.elements, &s.first_free, [&](const char* p){
os << *p;
});
return os;
}
main.cpp:
#include "string.h"
#include <iostream>
int main()
{
String s("1234");
std::cout << s <<"\n";
return 0;
}
Output:
1
Press <RETURN> to close this window...
Why is the output like so? why not 1234?
Probably because elements points to an array of char, so each element is a char, not a char*.
You also need to drop the & in front of s.elements and s.first_free, because you are interested in the addresses the pointers point to, not the addresses of the pointers themselves.
So, this code would work:
std::for_each(s.elements, s.first_free, [&](char p){
os << p;
});
As mentioned by #TemplateRex in comments, it would be both cleaner and more idiomatic to use the begin() and end() member functions:
std::for_each(s.begin(), s.end(), [&](char p){ os << p; });
sizeof(pointer) where pointer is a char const* does not return the length of an array. You make this mistake multiple times. Use strlen instead. This is hidden because your string is 4 char long, and on a 32 bit system sizeof(ptr) is 4.
Next &first_free and similar in your for_each should be just first_free.
Next your lambda should take char not char*s. Then the output should be << p not << *p.
You should create both const and non-const begin and end. const returns char const *, while non const returns char* -- containers that logically own their underlying data should use const that way for iteration.
Next replace your for_eaxh to use begin() and end() like for_each( x.begin(), x.end(), ... -- no need to redo what begin and end do. In C++11 you can even use a ranged based for:
for(char c : s ) {
std::cout << c
}
instead of for_each.