So I was wondering if I could do something like the following in C++.
#include <string>
using namespace std;
class foo
{
public:
string bar;
string baz;
foo(const string &faz)
:bar(faz)
{
};
};
int main()
{
const foo foo1("somestring");
bool isTrue = ((std::string(foo1) == "some string");//This should be true
return 0;
}
How would I make std::string(foo1) equal "some string"? Is there some special variable name I need to use? Or is there something else I need to be doing? Please tell me if you know
You've basically got 2 options:
add a conversion operator, so that a foo can implicitly convert to string
define operator== for foo.
The first option may seem attractive at first, but code that uses conversion operators can quickly get out of hand - overload resolution is tricky enough as it is without adding more possible pathways.
The operator== version is quite simple (note, by making it a non-member you allow the char array to appear on the left of the ==):
bool operator==( foo const &f1, foo const &f2 )
{
return f1.bar == f2.bar;
}
You would use it like:
bool isTrue = (foo1 == "some string");
This matches char arrays because foo has a converting constructor, and the implicit conversion chain of char[] -> char * -> const std::string & happens.
Related
I have a template function, within which I need to convert a const char * to my template value.
I know for a fact that this const char * was originally read from a ascii text file. My current code looks like this:
template <typename T>
bool Get(T &value, std::string const &query, T const &default)
{
const char* result = DataHandler.GetValue(query);
if (result != NULL)
{
value = static_cast<T>(result); //Here is the issue
return true;
}
value = default
return false;
}
Using this with an int as example i get the error
error C2440: 'static_cast' : cannot convert from 'const char *' to 'int'
Is there a way I can convert the char* to my type T seamlessly, I could not find an answer for this on SO.
In the worst case I can make a case for the 10 types I expect and give an error if not one of these, but I would rather not do it that way if possible.
Is there a way I can convert the char* to my type T seamlessly,
No.
There is no way to make conversion from string to a type automatically work with all types. Such conversion has to be implemented for each class. Typically, it is done by implementing the stream extraction operator >> for std::istream. The built in types such as int and some standard types such as std::string already have such operator. Then you can do for example:
std::istringstream istream(result);
int i;
istream >> i;
Without knowing exactly what you are asking for, using the answer given by #eerorika, here is a generic way to do the conversion:
#include <sstream>
#include <string>
#include <iostream>
template <typename T>
struct DefaultConverter
{
static T Convert(const char *result)
{
std::istringstream istream(result);
T i;
istream >> i;
return i;
}
};
template <typename T, typename Converter = DefaultConverter<T>>
bool Get(T &value, std::string const &query, T const &defaultVal)
{
const char* result = "100";
value = Converter::Convert(result);
return true;
}
int main()
{
int test = 10;
Get(test, "abc123", test);
std::cout << test;
}
The code referring to the DataHandler function is not important to illustrate what the above does.
Basically, the Get function takes an additional argument, namely a conversion template that has an available Convert function that can be called by Get. Note that the default converter simply uses the code as illustrated in the previous answer given by #eerorika.
This gives you the flexibility of providing your own converter that has a Convert function that can do anything customized.
For example:
struct SomeMPEGObject
{
SomeMPEGObject() {}
SomeMPEGObject(const char *) {}
};
struct MPEGConverter
{
static SomeMPEGObject Convert(const char *result)
{
return SomeMPEGObject(result);
}
};
//...
SomeMPEGObject mpeg;
Get<SomeMPEGObject, MPEGConverter>(mpeg, "12345", mpeg);
This will work without changing any code in the Get function.
In many of my unit tests I need to compare the contents of simple structs having only data members:
struct Object {
int start;
int stop;
std::string message;
}
Now, if I want to write something like:
CHECK(object1==object2);
I always have to implement:
bool operator==(const Object& lhs, const Object& rhs) {
return lhs.start==rhs.start && lhs.stop==rhs.stop && lhs.message=rhs.message;
}
Writing all these comparison functions becomes tedious, but is also prone to errors. Just imagine, what will happen if I add a new data member to Object, but the comparison operator will not be updated.
Then I remembered my knowledge in Haskell and the magic deriving(Eq) directive, which just generates a sane comparison function for free.
How, could I derive something similar in C++?
Happily, I figured out that C++17 comes with a generic operator== and that every struct should be easily convertible to an std::tuple by the virtue of std::make_tuple.
So I boldly tried the following:
#include <tuple>
#include <iostream>
#include <tuple>
template<typename T>
bool operator==(const T& lhs, const T& rhs)
{
auto leftTuple = std::make_tuple(lhs);
auto rightTuple = std::make_tuple(rhs);
return leftTuple==rightTuple;
}
struct Object
{
std::string s;
int i;
double d;
};
int main(int arg, char** args)
{
std::cout << (Object{ "H",1,2. } == Object{ "H",1,2. }) << std::endl;
std::cout << (Object{ "A",2,3. } == Object{ "H",1,2. }) << std::endl;
return EXIT_SUCCESS;
}
But, unfortunately it just doesn't compile and I really don't know why. Clang tells me:
main.cpp:11:18: error: use of overloaded operator '==' is ambiguous (with operand types
'std::tuple<Object>' and 'std::tuple<Object>')
return leftTuple==rightTuple;
Can I possibly fix this compile error to get my desired behavior?
No, since comparing tuples reverts to comparing the elements of the tuple, so leftTuple == rightTuple tries to compare two Objects which is not possible.
that every struct should be easily convertible to an std::tuple by the virtue of std::make_tuple
No, you'll just get a tuple with one element, the struct.
The trick is to use std::tie:
std::tie(lhs.mem1, lhs.mem2) == std::tie(rhs.mem1, rhs.mem2)
but that has the same problem as your original solution. Unfortunately C++17 doesn't have any facility to avoid this problemyou could write a macro :). But in C++20 you will be able to do:
struct Object
{
std::string s;
int i;
double d;
bool operator==(const Object &) const = default;
};
which will generate the correct comparison operators for Object.
I am trying to overload the subscript operator -i know it as the element access operator- to take a char * or a std::string.
I have a struct
struct foo
{
int Age;
const char *Name;
};
and a std::vector that will be holding multiple instances of this struct.
std::vector<foo>bar;
my goal is to be able to access each foo in bar by calling them by their name.
std::cout<<"Simon's age is: "<<bar["simon"];
I've just been searching google for a long while trying to find an example or something to go off, with no luck.
i thought that this would work
foo operator[](char *Name)
{
for(int i=0;i<bar.size();i++)
{
if(bar[i].Name == Name){return bar[i];}
}
}
however apparently i'm doing something wrong
I do know this could be done with an std::map but i'd rather use an std::vector, thanks.
I would greatly appreciate your help however you choose to offer it. Thank you .
To do what you want requires inheriting from std::vector. Otherwise you can't overload its methods.
Something like the following (untested).
struct fooVector : public std::vector<foo> {
typedef std::vector<foo> super;
using super::size;
...
foo& operator[](char const* Name) {
super& self=*this;
for(int i=0;i<size();i++)
{
if( ! strcmp( self[i].Name, Name) ){return self[i];}
}
// Need to do something reasonable if not found
}
};
bar[i].Name == Name
was probably meant to be
strcmp(bar[i].Name, Name) == 0
However, you'll be better off using std::strings than managing plain char pointers.
And instead of inheriting form a vector, create a class with a vector as a member and have your operator[] on that class.
First of all there are some problems with your test:
1) you're using const char* for string type, comparing with == would likely fail as you're comparing two pointers and not their content. you need to use strcmp or better use std::string (c++ way)
2) what if your index operator would not find the value you're looking for?
I don't think this is a proper way of doing this but in case you really want to use vector you can inherit your own class and define new operator which uses const char* as a index argument:
#include <vector>
#include <iostream>
#include <string.h>
struct foo {
int age;
const char *name;
};
class MyVector : public std::vector<foo> {
public:
const foo* operator[](const char* name) const {
for (auto it=cbegin(); it != cend(); ++it) {
if (strcmp(it->name, name) == 0) {
return &(*it);
}
}
return nullptr;
}
};
int main(int argc, char *argv[]) {
foo foo1 = { 10, "abc" };
foo foo2 = { 20, "test" };
MyVector v;
v.push_back(foo1);
v.push_back(foo2);
std::cout << "test: " << v["test"]->age << std::endl;
}
Although it's not generally advised to inherit from stl containers (they don't have virtual destructors), if you don't add any data attributes to inherited class you should be fine.
But I would suggest you to consider using std::map as a container and std::string as index / name attribute type. Searching vector has linear complexity but std::map has logaritmic complexity. Or you can consider using hash tables.
I have a question related to operator overloading, and it is easy to define a class as well as its operator overloading function as the following codes illustrate:
typedef std::vector<std::vector<int> > ARRAY;
class ABC
{
public:
ABC():a(0)
{
};
int a;
ABC& operator = (int value)
{
a = value;
return *this;
}
ABC(int value)
{
a = value;
}
};
void obtain_priority_array(const std::vector<double> &weighting, const ABC &priority_array=NULL)
{
}
int main()
{
vector<double> weighting;
weighting.push_back(0.8);
weighting.push_back(0.9);
weighting.push_back(0.6);
weighting.push_back(0.3);
weighting.push_back(0.5);
ABC test;
obtain_priority_array(weighting, test);
return 0;
}
In the above example, class ABC redefined operator = so that the function void obtain_priority_array(const std::vector<double> &weighting, const ABC &priority_array=NULL) can have a default argument const ABC &priority_array=NULL. My question is if the last parameter in the function comes from STL, for example, const std::vector<int> &priority_array=NULL, how can we redefine operator =. Thanks!
EDIT:
void obtain_priority_array(const std::vector &weighting, const std::vector<int> &sample=NULL) failed!
Your misconceptions start with the proposal to add operator= to allow for a default argument of that type. In your example, it is not operator= being called, but ABC(int).
The reason your code isn't being accepted when using std::vector is that NULL translates to 0 (at least it does almost all of the time you'll see it), and the only constructor of std::vector that can take 0, the one taking a count of how many items, is marked explicit.
To fix the immediate problem, the syntax could be changed to:
const std::vector<int> &priority_array = std::vector<int>(0)
However, this introduces different semantics. By your use of NULL, it looks like you were expecting it to represent no vector. This version will provide an empty vector for use if none is given. It will not be no vector at all. If you want that distinction, you should use boost's optional library, or a simple pointer, as references are not the right tool.
References cannot be NULL, your problem has nothing to do with operator overloading. If you want to be able to handle NULL as the default value, switch the parameter type from reference to pointer.
void obtain_priority_array( const std::vector<double>& weighting,
const ABC *priority_array = NULL)
{
if( priority_array == NULL ) {
// blah
} else {
// more blah
}
}
Another option is to use something like Boost.Optional to represent the optional parameter.
typedef boost::optional<ABC> maybe_ABC;
void obtain_priority_array( const std::vector<double>& weighting,
const maybe_ABC& priority_array = maybe_ABC() )
{
if( !priority_array ) {
// blah
} else {
// more blah
}
}
When you use = to create a reference, you're not calling operator= at all. You're initializing the reference.
Instead of using NULL you can create a static instance of the class to represent a null value.
static const ABC ABC_NULL;
void obtain_priority_array(const std::vector<double> &weighting, const ABC &priority_array=ABC_NULL)
{
if (&priority_array == &ABC_NULL) // the default was used
Of course it would be easier to just use a pointer instead of a reference.
As a C++ beginner I want to write some simple type casts. It there a way to create casting logic which can be used in the type new = (type)old format with the prefix parentheses?
string Text = "Hello";
char* Chars = "Goodbye";
int Integer = 42;
string Message = Text + (string)Integer + (string)Chars + "!";
I'd like to stick with this syntax if possible. For example the string cast of boost int Number = boost::lexical_cast<int>("Hello World") has an unattractive long syntax.
Just use a normal function that you overload for different types:
std::string str(int i) {
return "an integer";
}
std::string str(char* s) {
return std::string(s);
}
Then use if not like a cast, but as a normal function call:
string Message = Text + str(Integer) + str(Chars) + "!";
It's the most common thing in C++ to cast using a NAME<TYPE>(ARGUMENT) syntax, like in static_cast<int>(char). It makes sense to extend this the way boost does.
However, if you want to convert non-primitive types, you can use non-explicit constructors with a single argument and the cast operator.
class MyType {
public:
MyType(int); // cast from int
operator int() const; // cast to int
};
This is not possible if you are dealing with already existing types.
You cannot change the behaviour of the C-style cast. C++ will make up its mind how to interpret such a cast.
You could however come up with an intermediate type that shortens the syntax:
template <typename From>
struct Cast {
From from;
Cast(From const& from) : from(from) {}
template <typename To>
operator To() const {
return convert(from,To());
}
};
template <typename From>
Cast<From> cast(From const& from) {
return Cast<From>(from);
};
std::string convert(int, std::string const&);
This would allow you to convert things explicitly but without stating how exactly:
int i = 7;
std::string s = cast(i);