I have C++ class similar to this:
class A{
std::string str;
public:
A(std::string &str) : str(str){}
int cmpAt(const std::string &key) const{
return str.cmp(key);
}
int cmpAt(const char *key) const{
return str.cmp(key);
}
}
Both cmpAt methods look the same. Is there some way NOT to duplicate the method? Perhaps with template ?
You should just write a function template:
template <typename K>
int cmpAt(K const& key) const {
return str.compare(key);
}
That way, if you call cmpAt with a const char*, you can avoid the overhead of having to construct an extra std::string.
EDIT Never mind, you're out of luck:
int compare(const charT* s) const;
5 Returns: compare(basic_string(s)).
So just delete the const char* overload - it provides you nothing of extra value over the std::string const& overload. You would have to write your own compare function to avoid the extra string constructor, at which point it's not code duplication anymore.
I would use boost::string_ref or your favourite other implementation of it
class A{
std::string str;
public:
A(std::string &str) : str(str){}
int cmpAt(const boost::string_ref &key) const{
return key.compare(str) * -1;
}
}
This will not create temporary strings and you can pass string literals and std::string.
When int cmpAt(const std::string &key) is called with const char*, key will be constructed with const char*. so you can simply delete cmpAt(const char *key).
As others have correctly pointed out, in your specific case, there's no need for a non-const comparison function.
However, in the general case, you can do the following:
RetType someMethod(params...) const {
return LongAndComplexComputation(params, ...);
}
// Return type deduction is C++14.
// If you can't use C++14, only C++11, the return type should be:
// const std::remove_reference<decltype(*this)>::type *
auto cthis() const {
return this;
}
RetType someMethod(params...) {
return cthis()->someMethod(params, ...)
}
If necessary, you will have to cast away const-qualification from the return type (e.g. when you are returning a pointer inside *this), using const_cast.
Related
Reading the answer for one exercise in C++ Primer, 5th Edition, I found this code:
#ifndef CP5_ex7_04_h
#define CP5_ex7_04_h
#include <string>
class Person {
std::string name;
std::string address;
public:
auto get_name() const -> std::string const& { return name; }
auto get_addr() const -> std::string const& { return address; }
};
#endif
What does
const -> std::string const&
mean in this case?
auto get_name() const -> std::string const& { return name; } is trailing return type notation for the equivalent
std::string const& get_name() const { return name; }
Note that the equivalence is exact in the sense that you can declare a function using one syntax and define it with the other.
(This has been part of the C++ standard since and including C++11).
The part -> std::string const& is trailing return type and is new syntax since C++11.
The first const says it a const member function. It can be safely called on a const object of type Person.
The second part simply tells what the return type is - std:string const&.
It is useful when the return type needs to be deduced from a template argument. For known return types, it is no more useful than than using:
std::string const& get_name() const { return name; }
It all makes more sense when you see an example where it actually matters; as written in the question, it's just an alternative way to declare the return type.
If you have a template function where you can not know the return type in advance, this can actually help. For example:
template <class X, class Y> auto DoSomeThing(X x, Y y) -> decltype(x * y);
You don't know what types X and Y actually are but you know the return value will have the same type as x * y which can be deduced in this way.
const is a cv-qualifier of the usual kind for the member function : *this is const inside the function.
-> std::string const& pairs with auto to form a trailing return type (see (2)). The difference is only syntactic here -- the following syntax is equivalent:
std::string const& get_name() const { return name; }
I have code similar to this:
class Pair{
public:
Pair(const void *blob);
//...
int cmp(const std::string &key) const;
int cmp(const Pair &pair) const;
}
Later if I do:
Pair p = ...;
p.cmp("Hello");
It won't compile, because conversion from const char * is ambiguous.
It can be translated to std::string and cmp(std::string) to be invoked, or...
it can be translated to Pair and cmp(const Pair) to be invoked.
I can not do constructor explicit.
I tried to do:
Pair(const char *blob) = deleted;
But then I can not construct the class with nullptr and I want to be able to do so.
As final solution, I defined following method:
int cmp(const char *key) const{
return cmp( std::string{ key } );
}
and it works fine.
Is there better way to deal with such class conversions?
Your “final solution” looks pretty good to me. Not constructing a temporary will also help improve performance. If your strings are known not to contain NUL characters, you can implement the version taking a std::string as argument as
inline int
cmp(const std::string& s)
{
return this->cmp(s.c_str());
}
which is too simple to worry about.
Making Pair's constructor explicit (I assume the “implicit” is a typo) sounds a good idea to me, too. Why can't you do it?
You can make the constructor explicit.
explicit Pair(const void *blob) { ... }
Then,
p.cmp("Hello");
will resolve to
int cmp(const std::string &key) const;
I am working on some project and have problems with duplicating function code. Is there a way to declare only single signature of function which will work for both const and non-const params? Implementation of const and non-const functions are the same.
#include <iostream>
template <class Data>
struct Processor {
int process(const Data &data)
{
return 42;
}
int process(Data &data)
{
return 42;
}
};
int main() {
using data_type = int;
data_type non_const_data = 1;
const data_type const_data = 2;
std::cout << Processor<data_type>().process(non_const_data) << std::endl;
std::cout << Processor<data_type>().process(const_data) << std::endl;
return 0;
}
Example http://ideone.com/tv0TsF
THANKS FOR YOUR ANSWERS
UPDATE
And what about this example?
#include <iostream>
#include <vector>
template <class Container, class Function>
struct Invoker {
const Container& invoke(const Container &container, Function function)
{
for (auto &value : container) function(value);
}
Container& invoke(Container &container, Function function)
{
for (auto &value : container) function(value);
}
};
int main() {
std::vector<int> container {1, 2, 3};
auto fn = [](int val) {
std::cout << val << std::endl;
};
Invoker<decltype(container), decltype(fn)>().invoke(container, fn);
return 0;
}
http://ideone.com/KnyGdT
If implementation of both functions are same, then provide const parameter function only. It will work both for non-const and const object in your case.
However, if you want to keep the both the function, you can implement non-const parameter function in terms of const parameter function. It will help you to avoid duplicate code.
int process(const Data &data)
{
return 42;
}
int process(Data &data)
{
return process(static_cast<const Data&>data);
}
I'd suggest declaring only the most restrictive one, that being the one that expects a const param. Should there be an issue of sending a non-const pointer/reference into it, you can always make it const before calling the method.
Yes:
int process(const Data &data)
{
return 42;
}
This can be called with Data, Data &, and Data const &, and anything convertible to Data. A const reference can bind to a non-const object or reference.
It might not be exactly what you are looking for, but a similar situation arise in the case of const vs non-const member functions. Sometimes, say for instance in an access function, you would like to have two versions, one usable if the object is const and the other usable when the object is non-const.
struct MyString {
char & at(int index) {
if (index < size) {
return data[index];
else throw std::range_error("Index out of range");
}
char at(int index) const {
if (index < size) {
return data[index];
else throw std::range_error("Index out of range");
}
};
Now these functions look very similar, why can't I reuse one to implement the other?
The answer is you can, but it requires some very careful casting, first of all the general advice is to call the const-function from the non-const. As you can usually guarantee that calling a const function will be safe even from a non-const context. The call to the const-function will not actually change the object after all. (There are exceptions as with mutable variables, or the reverse of what I'm about to propose just now.)
The solution (according to More Effective C++), is use const cast to call the const version from the non-const:
const char & at(int index) const {
if (index < size) {
return data[index];
else throw std::range_error("Index out of range");
}
char & at(int index) {
return const_cast<char &>( static_cast<const MyString>(this)->at(index) );
}
This functions in two steps. First, cast this to a const pointer, that will allow us to invoke the const-version of at(). Then, once we return a const-reference to the element, cast it to a writeable reference.
Sorry, new to C++, converting from C, and have struggled to find a good way to do this...
//Fragment follows
const char *List1[]={"Choice1", "Not a good choice", "choice3"}; //rom-able
const char *List2[]={"Hello", "Experts", "Can", "You", "Help?"};
class ListIF{
private:
int index;
char *list;
public:
void GetValString(char *tgt,int len);//get parameter value as string max len chars
void SetIndex(int n){index = n;};
int GetIndex(void){return index;};
};
//end Fragment
The problem is how to write the constructor so that I can "encapsulate" the lists inside the class, without getting heap bloat (embedded target). And then how to write the gettor so that we can see list[index] within the class.
I am going daft trying to do something that seems obvious, so I am missing something?
In C++, prefer using std::string over const char*. It will solve most of your problems you face with const char*.
For an array of strings, use std::vector<std::string>. It will solve most of your problems you face with const char *[].
You can even initialize the std::vector with multiple strings as,
std::vector<std::string> List1(adder<std::string>("Choice1")("Not a good choice")("choice3"));
std::vector<std::string> List2(adder<std::string>("Hello")("Experts")("Can")("You")("Help?"));
Where adder<> is a class template defined as:
template<typename T>
struct adder
{
std::vector<T> items;
adder(const T &item) { items.push_back(item); }
adder& operator()(const T & item) { items.push_back(item); return *this; }
operator std::vector<T>&() { return items ; }
};
Sample running code here : http://www.ideone.com/GLEZr
/** Wrapper for C style arrays; does not take ownership of the array */
template <typename T>
class static_array
{
T *array;
size_t nelems;
public:
template <size_t N>
static_array(T (&a)[N]) : array(a), nelems(N) {}
T &operator[](size_t i) { return array[i]; }
T const &operator[](size_t i) const { return array[i]; }
size_t size() const { return nelems; }
};
typedef static_array<char const *> static_cstr_array;
Construct as static_cstr_array array1(List1). The setter is operator[], i.e.
array1[1] = "foo!";
You can add any method that you want to this class.
(I chose the name static_array because, as far as the class is concerned, the underlying array must be static: it should not grow, shrink or move due to realloc or otherwise. It doesn't mean the array must have static linkage.)
Not sure what you want your functions to be but one way to wrap the arrays would be:
EDIT : changed to incorporate Larsmans suggestion (on the chance that your compiler can't handle his answer).
class ListIF
{
private:
std::vector<const char*> m_list;//stores ptrs to the ROM
public:
ListIF(char const **list, size_t n) : m_list(list, list+n) {}
const char* get( int pos )
{
return m_list[pos];
}
};
Whew, that was a long title.
Here's my problem. I've got a template class in C++ and I'm overloading the [] operator. I have both a const and a non-const version, with the non-const version returning by reference so that items in the class can be changed as so:
myobject[1] = myvalue;
This all works until I use a boolean as the template parameter. Here's a full example that shows the error:
#include <string>
#include <vector>
using namespace std;
template <class T>
class MyClass
{
private:
vector<T> _items;
public:
void add(T item)
{
_items.push_back(item);
}
const T operator[](int idx) const
{
return _items[idx];
}
T& operator[](int idx)
{
return _items[idx];
}
};
int main(int argc, char** argv)
{
MyClass<string> Test1; // Works
Test1.add("hi");
Test1.add("how are");
Test1[1] = "you?";
MyClass<int> Test2; // Also works
Test2.add(1);
Test2.add(2);
Test2[1] = 3;
MyClass<bool> Test3; // Works up until...
Test3.add(true);
Test3.add(true);
Test3[1] = false; // ...this point. :(
return 0;
}
The error is a compiler error and the message is:
error: invalid initialization of non-const reference of type ‘bool&’ from a temporary of type ‘std::_Bit_reference’
I've read up and found that STL uses some temporary data types, but I don't understand why it works with everything except a bool.
Any help on this would be appreciated.
Because vector<bool> is specialized in STL, and does not actually meet the requirements of a standard container.
Herb Sutter talks about it more in a GOTW article: http://www.gotw.ca/gotw/050.htm
A vector<bool> is not a real container. Your code is effectively trying to return a reference to a single bit, which is not allowed. If you change your container to a deque, I believe you'll get the behavior you expect.
A vector<bool> is not implemented like all other vectors, and does not work like them either. You are better off simply not using it, and not worrying if your code can't handle its many peculiarities - it is mostly considered to be A Bad Thing, foisted on us by some unthinking C++ Standard committee members.
Some monor changes to your class should fix it.
template <class T>
class MyClass
{
private:
vector<T> _items;
public:
// This works better if you pass by const reference.
// This allows the compiler to form temorary objects and pass them to the method.
void add(T const& item)
{
_items.push_back(item);
}
// For the const version of operator[] you were returning by value.
// Normally I would have returned by const ref.
// In normal situations the result of operator[] is T& or T const&
// But in the case of vector<bool> it is special
// (because apparently we want to pack a bool vector)
// But technically the return type from vector is `reference` (not T&)
// so it you use that it should compensate for the odd behavior of vector<bool>
// Of course const version is `const_reference`
typename vector<T>::const_reference operator[](int idx) const
{
return _items[idx];
}
typename vector<T>::reference operator[](int idx)
{
return _items[idx];
}
};
As the other answers point out, a specialization is provided to optimize for space allocation in the case of vector< bool>.
However you can still make your code valid if you make use of vector::reference instead of T&. In fact it is a good practice to use container::reference when referencing data held by a STL container.
T& operator[](int idx)
becomes
typename vector<T>::reference operator[](int idx)
Of course ther is also a typedef for const reference:
const T operator[](int idx) const
and this one becomes (removing the useless extra copy)
typename vector<T>::const_reference operator[](int idx) const
The reason for the error is that vector<bool> is specialized to pack the boolean values stored within and vector<bool>::operator[] returns some sort of proxy that lets you access the value.
I don't think a solution would be to return the same type as vector<bool>::operator[] because then you'd be just copying over the regrettable special behavior to your container.
If you want to keep using vector as the underlying type, I believe the bool problem could be patched up by using a vector<MyBool> instead when MyClass is instantiated with bool.
It might look like this:
#include <string>
#include <vector>
using namespace std;
namespace detail
{
struct FixForBool
{
bool value;
FixForBool(bool b): value(b) {}
operator bool&() { return value; }
operator const bool& () const { return value; }
};
template <class T>
struct FixForValueTypeSelection
{
typedef T type;
};
template <>
struct FixForValueTypeSelection<bool>
{
typedef FixForBool type;
};
}
template <class T>
class MyClass
{
private:
vector<typename detail::FixForValueTypeSelection<T>::type> _items;
public:
void add(T item)
{
_items.push_back(item);
}
const T operator[](int idx) const
{
return _items[idx];
}
T& operator[](int idx)
{
return _items[idx];
}
};
int main(int argc, char** argv)
{
MyClass<string> Test1; // Works
Test1.add("hi");
Test1.add("how are");
Test1[1] = "you?";
MyClass<int> Test2; // Also works
Test2.add(1);
Test2.add(2);
Test2[1] = 3;
MyClass<bool> Test3; // Works up until...
Test3.add(true);
Test3.add(true);
Test3[1] = false; // ...this point. :(
return 0;
}