Can the const_cast be used for creating non-const versions of already implemented methods? I think I saw something along these lines (with recommendation for the const method to do the actual work), but I'm not really sure how it's supposed to work.
Value& const search(key) const {
// find value with key
return value;
}
Value& search(key) {
return const_cast<Value&>(search(key));
}
If not this way, what is recommended way of creating non-const functions without code duplicity?
The easiest way to do it is with as_const from C++17:
Value& search(key) {
return const_cast<Value&>(std::as_const(*this).search(key));
}
Without it you can do this instead (or implement it yourself, it's not very hard)
Value& search(key) {
return const_cast<Value&>(static_cast<const T&>(*this).search(key));
}
Where T is the type of your class (you can have a generic solution with decltype but it gets really ugly due to decltype(*this) being a reference type).
You can take a look at the as_const implementation here or the generic cast here.
Two approaches.
First:
namespace notstd{ // backported C++17
template<class T>
T const& as_const(T& t){return t;}
template<class T>
T const&& as_const(T&& t){return t;}
}
namespace utility { // not ever in std
template<class T>
T& remove_const(T const& t){return const_cast<T&>(t);}
template<class T>
T&& remove_const(T const&& t){return const_cast<T&&>(t);}
}
then:
Value& const search(Key key) const {
// find value with key
return value;
}
Value& search(Key key) {
return utility::remove_const(notstd::as_const(*this).search(key));
}
or alternatively:
Value& const search(Key key) const {
return search(*this, key);
}
Value& search(Key key) {
return search(*this, key);
}
private:
template<class Self>
friend decltype(auto) search(Self& self, Key key){
// find value with key
}
where we delegate the work to a friend template where self is maybe-const.
Related
I tried to iterate over an std::optional:
for (auto x : optionalValue)
{
...
}
With the expectation of it doing nothing if optionalValue is empty but doing one iteration if there is a value within, like it would work in Haskell (which arguably made std::optional trendy):
forM optionalValue
( \x ->
...
)
Why can't I iterate an optional? Is there another more standard C++ method to do this?
std::optional does not have a begin() and end() pair. So you cannot range-based-for over it. Instead just use an if conditional.
See Alternative 2 for the closest thing to what you want to do.
Edit: If you have a temporary from a call result you don't have to check it explicitly:
if (auto const opt = function_call()) {
do_smth(*opt);
}
The check for static_cast<bool>(opt) is done implicitly by if.
Alternative 1
Another alternative is to not use an std::optional<T> but std::variant<std::monostate, T>. You can then either use the overloaded idiom or create a custom type to handle the monostate:
template <typename F>
struct MaybeDo {
F f;
void operator()(std::monostate) const {}
template <typename T>
void operator()(T const& t) const { f(t); }
};
which will allow you to visit the value with some function:
std::variant<std::monostate, int> const opt = 7;
std::visit(MaybeDo{[](int i) { std::cout << i << "\n"; }}, opt);
Alternative 2
You can also wrap optional in a thing that allows you to iterate over it. Idea:
template <typename T>
struct IterateOpt {
std::optional<T> const& opt;
struct It {
std::optional<T> const* p;
It& operator++() {
p = nullptr;
return *this;
}
It operator++(int) {
return It{nullptr};
}
auto const& operator*() const { **p; }
};
auto begin() const {
if (opt) return It{&opt};
else end();
}
auto end() const {
return It{nullptr};
}
};
This is a crude sketch of how to do this and probably requires some love to work on different situations (like a non-const optional).
You can use this to "iterate" over the optional:
for (auto const& v: IterateOpt{function_call()}) {
do_smth(v);
)
The title of this question used to be: Are there practical advantages to creating an iterator class compared to returning raw pointers from begin and end functions?
Recently I have been working on a code base which uses MFC and objects such as CArray<T, U>.
Some parts of new code which has been written make use of the STL and <algorithm> library.
For example
CArray<int int> carray;
carray // do stuff
std::vector<int> stlvector(begin(carray), end(carray));
stlvector.dostuff() // do stuff
I recently asked a question about creating iterators for a class such as CArray, which I do not have access to.
I now have some further questions about this. The first question can be found here. Here is my second question:
Should the begin and end functions return raw pointers or iterators?
In the linked question above, an example was provided as an answer which returns raw pointers. This answer was very similar to the implementation I used.
template<typename T, typename U>
auto begin(const CArray<T, U> &array>)
{
return &array[0];
}
template<typename T, typename U>
auto end(const CArray<T, U> &array>)
{
return (&array[array.GetCount() - 1]) + 1;
}
These functions return raw pointers. However I attempted to implement an iterator solution. So far I have not been successful.
The main reference which I used during my research can be found here:
https://internalpointers.com/post/writing-custom-iterators-modern-cpp
First attempt
This is the first attempt that I made in finding a solution.
You can play with this code here.
#include <iostream>
#include <iterator>
#include <algorithm>
template <typename U>
class CArrayForwardIt
{
using iterator_category = std::forward_iterator_tag;
using difference_type = std::ptrdiff_t;
using value_type = U;
using pointer = U*;
using reference = U&;
public:
CArrayForwardIt(pointer ptr)
: m_ptr(ptr)
{
}
// = default?
//CArrayForwardIt(CArrayForwardIt<U> other)
// : m_ptr(ptr)
// {
// }
reference operator*() const
{
return *m_ptr;
}
// what does this do, don't understand why operator-> is needed
// or why it returns a U* type
pointer operator->()
{
return m_ptr;
}
CArrayForwardIt& operator++()
{
++ m_ptr;
return *this;
}
CArrayForwardIt operator++(int)
{
CArrayForwardIt tmp(*this);
++ (*this);
return tmp;
}
friend bool operator==(const CArrayForwardIt& lhs, const CArrayForwardIt& rhs)
{
return lhs.m_ptr == rhs.m_ptr;
}
friend bool operator!=(const CArrayForwardIt& lhs, const CArrayForwardIt& rhs)
{
return !(lhs == rhs);
}
private:
pointer m_ptr;
};
template<typename T, typename U>
auto begin(const CArray<T, U> &array)
{
return CArrayForwardIt<U>(&array[0]);
}
template<typename T, typename U>
auto end(const CArray<T, U> &array)
{
return CArrayForwardIt<U>((&array[array.GetCount() - 1]) + 1);
}
int main()
{
CArray<int, int> c;
// do something to c
std::vector<int> v(begin(c), end(c));
return 0;
}
This is what happens when I try to compile this (with Visual Studio 2019 Pro).
no instance of constructor "std::vector<_Ty, _Alloc>::vector [with _Ty=int, _Alloc=std::allocator<int>]" matches argument list
'<function-style-cast>': cannot convert from 'contt TYPE*' to 'std::CArrayForwardIt<U>'
'std::vector<int, std::allocator<int>>::vector(std::vector<int, std::allocator<int>> &&, const _Alloc &) noexcept(<expr>)': cannot convert from argument 1 from 'void' to 'const unsigned int'
Being more familiar with gcc, I have little knowledge of how to understand this.
Second attempt
I made another two further attempts at this but they were quite similar.
One was to change my class CArrayForwardIt to inherit from iterator<std::forward_iterator_tag, std::ptrdiff_t, U, U*, U&>, and to remove the using... lines at the top of the class. This didn't seem to get me any closer to a solution.
In addition, I looked at the constructor definition for std::vector. See here.
I may be misunderstanding here, but it looks like std::vector requires a InputIt type argument.
Therefore I tried to change my class to be something like this:
#include <iostream>
#include <iterator>
#include <algorithm>
template <typename U>
class forward_iterator
{
using iterator_category = std::forward_iterator_tag;
using difference_type = std::ptrdiff_t;
using value_type = U;
using pointer = U*;
using reference = U&;
public:
forward_iterator(pointer ptr)
: m_ptr(ptr)
{
}
// = default?
//forward_iterator(forward_iterator<U> other)
// : m_ptr(ptr)
// {
// }
reference operator*() const
{
return *m_ptr;
}
// what does this do, don't understand why operator-> is needed
// or why it returns a U* type
pointer operator->()
{
return m_ptr;
}
forward_iterator& operator++()
{
++ m_ptr;
return *this;
}
forward_iterator operator++(int)
{
forward_iterator tmp(*this);
++ (*this);
return tmp;
}
friend bool operator==(const forward_iterator& lhs, const forward_iterator& rhs)
{
return lhs.m_ptr == rhs.m_ptr;
}
friend bool operator!=(const forward_iterator& lhs, const forward_iterator& rhs)
{
return !(lhs == rhs);
}
private:
pointer m_ptr;
};
template<typename T, typename U>
auto begin(const CArray<T, U> &array)
{
return forward_iterator<U>(&array[0]);
}
template<typename T, typename U>
auto end(const CArray<T, U> &array)
{
return forward_iterator<U>((&array[array.GetCount() - 1]) + 1);
}
int main()
{
CArray<int, int> c;
// do something to c
std::vector<int> v(begin(c), end(c));
return 0;
}
This, perhaps unsurprisingly, did not compile either. At this point I became confused. std::vector appears to demand an InputIt type, which forward_iterator should work for, but it doesn't seem to make sense to redefine what forward_iterator is, even if I write this class outside of namespace std.
Question
I am fairly sure there should be a way to write an iterator class for the MFC CArray, which can be returned by begin and end functions. However, I am confused as to how to do this.
Further to the question of writing a working solution, I am beginning to wonder if there are any practical advantages to doing this? Does what I am trying to do even make sense? The raw pointer solution clearly works, so are there any advantages of investing the effort to write an iterator based solution? Can iterator solutions provide more sophisticated bounds checking, for example?
Since I managed to get this working I wanted to post a solution, hopefully I don't make too many errors transcribing it.
One thing that was not shown in the above code snippet is the fact that all these class and function definitions existed inside of namespace std. I posted another question about this earlier, and was informed that these things should not be inside namespace std. Correcting this seems to have resolved some problems and made the solution a step closer.
You can find that question here.
This is what I have so far: This is how to write an iterator for an external class which the programmer does not have access to. It also works for your own custom types or containers which you do have access to.
// How to write an STL iterator in C++
// this example is specific to the MFC CArray<T, U> type, but
// it can be modified to work for any type, note that the
// templates will need to be changed for other containers
#include <iterator>
#include <mfc stuff...>
template<typename T, typename U>
class CArrayForwardIt : public std::iterator<std::forward_iterator_tag, std::ptrdiff_t, U, U*, U&>
{
// the names used in this class are described in this list
// using iterator_category = std::forward_iterator_tag;
// using difference_type = std::ptrdiff_t;
// using value_type = U;
// using pointer = U*;
// using reference = U&;
public:
CArrayForwardIt(CArray<T, U> &array_ref, const std::size_t index)
: m_array_ref(array_ref)
, m_index(index)
{
}
// the only way I could get this to work was to make the return type
// an explicit U&, I don't know why this is required, as using
// reference operator*() const did not seem to work
U& operator*() const
{
if(m_index < m_array_ref.GetCount())
{
return m_array_ref[m_index];
}
else
{
throw std::out_of_range("Out of range Exception!");
}
}
CArrayForwardIt& operator++()
{
++ m_index;
return *this;
}
CArrayForwardIt operator++(int)
{
CForwardArrayIt tmp(*this);
++(*this);
}
friend bool operator==(const CArrayForwardIt& lhs, const CArrayForwardIt& rhs)
{
if(&(lhs.m_array_ref) == &(rhs.m_array_ref))
{
return lhs.m_index == rhs.m_index;
}
return false;
}
friend bool operator!=(const CArrayForwardIt& lhs, const CArrayForwardIt& rhs)
{
return !(lhs == rhs);
}
private:
std::size_t m_index;
CArray<T, U> &m_array_ref;
};
template<typename T, typename U>
auto begin(CArray<T, U> &array)
{
return CArrayForwardIt<T, U>(array, 0);
}
template<typename T, typename U>
auto end(CArray<T, U> &array)
{
return CArrayForwardIt<T, U>(array, array.GetCount());
}
int main()
{
CArray<int, int> array;
// do stuff to array
// construct vector from elements of array in one line
std::vector<int> vector(begin(array), end(array));
// also works with other STL algorithms
}
Note my comment about the U& operator* which produced some compiler error when written as reference operator* which might be a Visual Studio compiler bug. I'm not sure about this.
I would suggest that although this method is more difficult to implement (but not much when you know how to do it) it has the advantage of not using raw pointers which means that the iterator functions can provide proper exception throwing statements when illegal operations are attempted. For example, incrementing the iterator when it is already at the end.
Useful references:
https://lorenzotoso.wordpress.com/2016/01/13/defining-a-custom-iterator-in-c/
https://internalpointers.com/post/writing-custom-iterators-modern-cpp
For completeness, here is the simpler solution using raw pointers.
template<typename T, typename U>
auto begin(CArray<T, U> &array)
{
return &(array[0]);
}
template<typename T, typename U>
auto end(CArray<T, U> &array)
{
// get address of last element then increment
// pointer by 1 such that it points to a memory
// address beyond the last element. only works for
// storage containers where higher index elements
// are guaranteed to be at higher value memory
// addresses
if(array.GetCount() > 0)
{
return &(array[array.GetCount() - 1]) + 1;
}
else
{
return &(array[0]) + 1;
}
}
You can use these in the same way as demonstrated in the other answer, however there is also a way to use STL vector without the begin and end functions:
CArray<int, int> array; // ... do something to array
std::vector<int> vec(&array[0], &(array[array.GetCount() - 1]) + 1);
// note only works if elements guaranteed to be in continuous
// packed memory locations
but it also works with begin and end which is nicer
std::vector<int> vec(begin(array), end(array));
My situation:
I frequently need to have a vector of structures where one field can be thought of as a Key or ID, and rather than store it expensively in a map (memory usage is very important in this app) I want to store it in a flat vector but present a map-like interface for finding elements by key.
My first solution to this problem:
template <class T, class Key, class KeyFn>
class TKeyedVector : public std::vector<T>
{
public:
const_iterator find(const Key& key) const {return std::find_if(begin(), end(), [&](const T& entry) {return keyFn(entry)==key; }); }
KeyFn keyFn;
};
struct KeyedDataEntry
{
std::string key;
int value;
struct KeyExtractor {
const std::string& operator()(const KeyedDataEntry& e) const {return e.key; };
};
};
using KeyedDataArray = TKeyedVector<KeyedDataEntry, std::string, KeyedDataEntry::KeyExtractor>;
Now this all works, but I would like to be able to remove the need for the KeyExtractor type by using the pointer to the member variable embedded into the type:
template <class T, class Key, Key T::* keyFn>
class TKeyedVector : public std::vector<T>
{
public:
const_iterator find(const Key& key) const {return std::find_if(begin(), end(), [&](const T& entry) {return keyFn(entry)==key; }); }
};
using KeyedDataArray = TKeyedVector<KeyedDataEntry, std::string, &KeyedDataEntry::key>;
However I can't get this to work. I've been looking at the implementation of std::mem_fn for clues, but I can't work out how to do it. The error I get with is something like:
warning C4353: nonstandard extension used: constant 0 as function expression. Use '__noop' function intrinsic instead
Any clues?
EDIT: sample version at http://ideone.com/Qu6TEy
Here is the start of a working solution. You don't need a special extractor object.
Note that I have encapsulated the vector. In time, you'll regret not doing this.
#include <vector>
#include <string>
template <class T, class Key, const Key& (T::*Extractor)() const>
class TKeyedVector
{
using storage = std::vector<T>;
using const_iterator = typename storage::const_iterator;
public:
decltype(auto) begin() const
{
return storage_.begin();
}
decltype(auto) end() const
{
return storage_.end();
}
const_iterator find(const Key& key) const
{
return std::find_if(begin(),
end(),
[&](const T& entry)
{
return entry.*Extractor() == key;
});
}
storage storage_;
};
struct KeyedDataEntry
{
std::string key;
int value;
const std::string& get_key() const { return key; }
};
int main()
{
TKeyedVector<KeyedDataEntry, std::string, &KeyedDataEntry::get_key> mymap;
}
But there is a problem with this idea of yours.
In order for this structure to be a map, the keys must be immutable. This argues for only returning immutable objects. This then argues immediately for simply using an unordered_set or set.
If you're going to return references to mutable objects in the underlying vector, then you may as well simply use std::find_if with a predicate to find them.
A pointer to member requires the pointer to member call syntax. (entry.*keyFn)()
C++17 will come with a standard std::invoke function to make writing such templates a bit less tiresome (it will work for all callable objects). But in the meanwhile, this is how you need to do this.
I've this class with a map attribute which values are boost::variant.
typedef boost::variant<char, int, bool, unsigned short, float, timeval, double > MultiType;
class A {
public:
template<class T>
T& operator[](const std::string& key) {
return boost::get<T>(map_[key]);
}
template<class T>
std::string keyTypeToString(const std::string& key) {
std::stringstream ss;
ss << boost::get<T>(map_[key]);
return ss.str();
}
private:
std::map<std::string, MultiType> map_;
};
From main:
A a;
a["param"];
Compiler report this errors:
../src/main.cpp:8:25: error: no match for ‘operator[]’ in ‘a["param"]’
../src/main.cpp:8:25: note: candidate is:
../src/util/A.h:53:5: note: template T& A::operator[](const string&)
Maybe I'm missing something trivial, but I can't understand where I'm wrong..
Start with this:
template<class T>
T& get(const std::string& key) {
return boost::get<T>(map_[key]);
}
You call this like a.get<int>("hello"), where it will get the element "hello" as an int.
Next, write this:
struct pseudo_ref {
std::string const& str;
A* a;
template<typename T>
operator T&()&&{
return a->get<T>(str);
}
template<typename T>
pseudo_ref operator=( T&& t ) && {
a->get<typename std::decay<T>::type>(str) = std::forward<T>(t);
return {str, a};
}
pseudo_ref(pseudo_ref const&)=delete;
pseudo_ref& operator=(pseudo_ref const&)=delete;
pseudo_ref( std::string const& s, A* a_ ):str(s), a(a_) {}
};
then back in A:
pseudo_ref operator[](std::string const& str) {
return {str, this};
}
and we get [] that magically converts for you, so long as you assign to it/read from it using the exactly correct type.
This is somewhat dangerous, but cool.
If you want a const pseudo_ref, you need another class to represent it (with no = and operator T const& instead of operator T&).
In practice, this kind of malarkey is rarely worth it.
I wrote this in C++11, because writing it in C++03 is slightly more painful (and runs into lifetime issues with pseudo_ref -- they still exist if you have an auto&& x = a["hello"]), and less pain is good.
class A {
public:
class proxy {
friend class A;
private:
MultiType& it;
proxy(MultiType& it): it(it) {}
public:
template<typename T>
operator T&() {
return boost::get<T>(it);
}
};
proxy operator[](const std::string& key) {
return proxy(map_[key]);
}
private:
std::map<std::string, MultiType> map_;
};
EXPLANATION:
I can see that Yakk was trying similar thing. I have encapsulated the MultiType& from map_[key] in the proxy and then left the work on conversion (type-cast) operator. That's all.
Simple a[""] without assignment gets you the proxy.
double d = a["double"] will try to convert the proxy to double and thus call proxy::operator double&() (I had to test it because I was not sure if the type deduction will work as it is or will need some more work - well, it works!)
AFTERNOTE: It was not clear from the question and code provided what operations are allowed. We can modify the proxy to allow other operations or make it more readonly by changing the signature of type-conversion operator to return const T& instead.
Allowing modification leads to question: why not using MultiType& directly? (returning it from A::operator[]) And that leads to question: why class A at all?
AFTERNOTE #2: boost::variant does not have type-conversion operator and there must have been a reason for it. Think about this code:
int i = a["double"]
Runtime exception! I think that best solution would be to sub-class the MultiType and define type-conversion operator there (while checking boost::variant::which()).
ASSIGNING TO ALREADY PRESENT NAMES:
class A { ...
class proxy { ...
template<class T> proxy& operator=(const T& rhs) {
it = rhs; return *this; }
...but the above can only work if we already have some value in the map.
class A { ...
A() { map_["pi"] = 3.14; } ...
a["pi"] = 3.1415;
COMPLETE REDISIGN:
class MultiType: public boost::variant<int, double, ...> {
public:
template<class T> operator T() {
switch(which()) {
case 0: return boost::get<int>(*this);
case 1: return boost::get<double>(*this);
...
Now we can use std::map<std::string, MultiType> directly (without class A or any proxy).
template<class T>
T& operator[](const std::string& key) {
return boost::get<T>(map_[key]);
}
There's no way for the compiler to deduce T from a call like a["param"];. You'd need to specify it explicitly
a.operator[]<int>("param");
which I doubt is what you're after, but what do I know.
I need someone who explain me this code, line by line. I specially don't understand this line:
operator std::map<T, U>()
Thank you very much.
template <typename T, typename U>
class create_map
{
std::map<T, U> m_map;
public:
create_map(const T& key, const U& val)
{
m_map[key] = val;
}
create_map<T, U>& operator()(const T& key, const U& val)
{
m_map[key] = val;
return *this;
}
operator std::map<T, U>()
{
return m_map;
}
};
operator std::map<T, U>()
{
return m_map;
}
This is user-defined conversion function.
That means, you can write this:
//obj is an object of type create_map
create_map<int,std::string> obj(1,"Nawaz");
//obj implicitly converts into std::map type!
std::map<int,std::string> map_inst=obj;
See this topic to know more about user-defined conversion function:
User Defined Conversions in C++
You can see this as well: Implicit conversion sequences (C++ only)
create_map<T, U>& operator()(const T& key, const U& val)
{
m_map[key] = val;
return *this;
}
This actually overloads the operator(), which internally inserts or updates (key,val) pair into the m_map; just see the function definition as to what it does.
So using this function you can write code like this,
obj(2,"Sarfaraz"); //this inserts the pair into the underlying m_map;
I would also suggest you to explore std::map a bit more, especially the overloaded operator[] in std::map.
Code:
template <typename T, typename U>
class create_map
{
std::map<T, U> m_map;
public:
create_map(const T& key, const U& val)
{
m_map[key] = val;
}
create_map<T, U>& operator()(const T& key, const U& val)
{
m_map[key] = val;
return *this;
}
operator std::map<T, U>()
{
return m_map;
}
};
The purpose of this code is to be able to specify a map with specific key/value pairs, by chaining calls to operator(), like
create_map<int, std::string>( 1, "blah" )( 2, "hah" )( 3, "doh" )
Since the class does not have a default constructor, there's no way to use it to create an empty map. That may be by design. Or it may be a design error.
The
operator std::map<T, U>()
{
return m_map;
}
defines a conversion to std::map<T, U>, which is the end result of it all.
It can be invoked implicitly wherever a create_map is specified and a std::map is expected, such as using a create_map expression as argument in a function call.
However, it can be pretty inefficient since it copies the map. The compiler may well optimize away the copying. But it’s ungood to needlessly rely on Quality Of Implementation (although sometimes that is the best that one can do).
So it should instead be
operator std::map<T, U> const& () const
{
return m_map;
}
The const at the end there allows a create_map to be declared as const and used.
With this conversion to reference there is the same problem as with using reference arguments, namely a theoretical possibility of aliasing, where code that retains a reference to const is not prepared to deal with changes of the referred object. But in practice it’s not a problem. For example, as formal argument one just writes std::string const& s (instead of just std::string s) as a matter of course, and very few if any errors result from that – I’ve never experienced any problem.
Cheers & hth.,
There's not much to understand about it. operator std::map<T, U>() overrides the class' conversion operator (which takes no parameters) to provide an object instance of type std::map<T, U>. std::map is a STL standard class for associative key->value storage. In your case it maps from keys of type T to values of type U. T and U have been undefined so far (you wrote template class, but where are the template parameters?)
The conversion operator allows to use the class instance in place of the type the operator provides conversion for, like this.
class foo {
operator char const *() {
return "foo instance as char const *";
}
};
// ...
void bar(foo &f)
{
// here the class instance is used as if it were a char const *
cout << f << endl;
}
The line
operator std::map<T, U>()
defines a function that will be called when your create_map object is used like an std::map somewhere in the code.
An easier example would be:
class A
{
public:
operator int()
{
return 3;
}
};
int main()
{
A a;
cout << a << endl;
}
Now the computer finds out that it doesn't know how to print variable a, but it knows how to convert it to an int and then print it. So "3" gets printed.