Const correctness in C++ - c++

I have simple data structure called array (similar to std::array). array class have method called all(). Here are method declarations:
const range<const_type_pointer> all() const;
range<type_point> all();
range is class template, which is constructed by two iterators. Here is simplified version of range class declaration:
template <typename Iterator>
class range {
typedef typename iterator_traits<Iterator>::type_value type_value;
typedef typename iterator_traits<Iterator>::type_reference type_reference;
typedef typename iterator_traits<Iterator>::type_pointer;
range(Iterator begin, Iterator end);
// methods & algorithms
// data members
mutable Iterator _begin;
mutable Iterator _end;
};
So basically, if I call method all on const array object, it should call const overload of method and return const range. Now, in my algorithms section, I have simple algorithm with following signature:
template <typename UnaryFunction>
range forEach(UnaryFunction function);
Then I tried following test:
void test1(const array<int, 10>& array)
{
auto r = array.all();
r.forEach([](int i) { std::cout << i << " "; });
}
And second one:
void test2(const array<int, 10>& array)
{
auto r = array.all();
r.forEach([](int& i) { ++i; });
}
In first case, where I was just printing variable i, compiler did not complained, even though I called non-const range method on const range object. In second case, compiler complained. Is this behaviour standard confornant?
As a compiler, I am using MSVS compiler.

First. Sice you are returning by value, it doesn't really make any difference if you are returning const range or range.
Second. auto r = array.all(); creates a copy of whatever was returned by all. Your r is a non-const object, and non-const forEach is used. But since the iterator is const in the second version, you can't modify the iterable.

auto works just like template argument deduction, which means that it drops top-level references and top-level const & volatile qualifiers. In other words, your r is of type range<const_pointer_type> in both cases.
Returning a const-qualified object should only be done in very special circumstances when you understand all details of its behaviour and really know that you want them.
Normally, you should just return objects without cv-qualifiers. It will play nicer with move semantics, and will not lead to incorrect expectations.

Related

Generating the necessary ref-qualified overloads for a member function

I have this class:
template<typename T, size_t N>
class Array {
private:
T array[N];
public:
template <typename... InitValues>
constexpr Array(InitValues... init_values)
: array{ init_values... } {}
[[nodiscard]]
consteval int len() const noexcept { return sizeof(array) / sizeof(T); }
}
I would like to know, for such a simple member function, when I should provide the necessary ref-qualified overloads.
With the actual code, I can compile and run the following code:
constexpr collections::Array a = collections::Array<long, 5>{1L, 2L, 3L};
SECTION("length of the array") {
REQUIRE( a.len() == 5 );
REQUIRE( collections::Array<int, 1>{1}.len() == 1 );
}
1- Why I can compile the second REQUIRE that contains the call with the rvalue?
Now I am gonna change the len() member function to this:
[[nodiscard]]
consteval int len() const& noexcept { return sizeof(array) / sizeof(T); }
2- Why I can compile both with the const&? I suppose that they are two are different ref-qualified usages. I assume that I can make the call with the first one, which is an lvalue, but can't understand why I can compile the second having defined the len() method as const&.
Last change:
[[nodiscard]]
consteval int len() const&& noexcept { return sizeof(array) / sizeof(T); }
And finally, I got a compiler error on a.get<I>().
'this' argument to member function 'len' is an lvalue, but function has rvalue ref-qualifier
REQUIRE( a.len() == 5 );
that works perfect if I comment that line of code and I just run:
REQUIRE( collections::Array<int, 1>{1}.len() == 1 );
and also I could use std::move(a) to perform the cast of a to an rvalue reference and make the code compile. But I don't want to do that.
What is the correct way of code those examples in terms of ref-qualified overloads?
Don't forget about the questions on the examples above
EDIT:
I will add another member function that could potentially do different things based on the ref-qualified implementation (or that what I am suppose that could happen):
template <size_t I>
requires concepts::AccessInBounds<I, N>
constexpr T get() const noexcept {
return array[I];
}
template <size_t I>
requires concepts::AccessInBounds<I, N>
constexpr T& get() const& noexcept {
return array[I];
}
To question 1: why not? The rule is the same as for lvalues: you can call const member functions regardless of the constness of the object.
To question 2: Because it is meant to be identical to having a const& function parameter: the function can be called with any lvalue or rvalue. It exists primarily to allow you to distinguish between lvalue and rvalue overloads:
class Array {
// These two declarations would be ambiguous for Array rvalues
// int len() const;
// int len() &&;
// These are not: your test expressions will use different overloads
int len() const&;
int len() &&;
};
The two functions in your edit are also ambiguous, for both lvalues and rvalues. A motivating example would be more along these lines: suppose my class provides functionality to some resource that could be expensive to copy, but is cheaper to move, say a std::vector.
template<class T>
class VectorView {
std::vector<T> vector;
public:
// ...
constexpr std::vector<T> const& base() const noexcept { return vector; }
};
Now there is no way for a user of this class to transfer ownership of the vector data back from a view object, even if that would be useful when calling the base() function on an rvalue. Because it is in the spirit of C++ to avoid paying for things you do not need, you could allow this by adding an rvalue-qualified overload that instead returns an rvalue reference using std::move.
So the answer to whether you need this kind of overload is it depends, which is unfortunately also in the spirit of C++. If you were implementing something like my example class for the standard library, then you certainly would, because it is based on std::ranges::owning_view. As you can see on that page, it covers all four possible base()s. If you were instead only using a reference to a source range, it would be unexpected and inappropriate to move from that object, so the related ref_view only has a const base() function like the one I wrote.
Edit As for move semantics, the difference between something like an array and a vector is that Array<T,N> is based on T[N], while std::vector<T> is based on T*. Moving the array requires N move operations (linear time complexity), and whether a move is an improvement over a copy depends on T. Also, it needs memory space for 2N elements. On the other hand, a vector only ever needs three pointers to do its job, so it can be moved in constant time, while copying still takes linear time.
This potential gain is the rationale for move semantics and rvalue references in a nutshell. The ability to also have &&-qualified member functions completes this language feature, but is not as significant as move constructors and assignment functions. I also found the answers to this question useful, as they give some more examples of ref-qualified overloads.

Matching constness of function argument for return type with concepts

C++ containers do not hold const elements, e.g. you have const std::vector<int>, not std::vector<const int>.
This is a bit unfortunate when I am trying to adjust type of return value of a function based of if passed container was const or not.
Here is motivating example, please do not focus too much on algorithm or use of boost, I only use it since C++ optional has not support for references.
This code appears to work, but code looks quite ugly, so I wonder if concepts give us a way to write this in a nicer way.
I presume not since basically concepts are just predicates, but I am hoping for something nice, in particular return type is quite spammy.
template<typename C>
using match_const = std::conditional_t< std::is_const_v<std::remove_reference_t<C>>,
const typename std::remove_reference_t<C>::value_type,
typename std::remove_reference_t<C>::value_type>;
// no constraints
auto ofind(auto& container, const auto& value) -> boost::optional<match_const<decltype(container)>&> {
if (auto it = std::ranges::find(container, value); it!=container.end()){
return *it;
}
return boost::none;
}
// dummy concept
template<typename C>
concept Container = requires (C c){
{c.begin()};
{c.end()};
{c.size()} -> std::same_as<size_t>;
};
// constraints version
auto ofind2(Container auto& container, const auto& value) ->boost::optional<match_const<decltype(container)>&>{
if (auto it = std::ranges::find(container, value); it!=container.end()){
return *it;
}
return boost::none;
}
If my question is too vague here is my idealized version:
boost::optional<Container::real_reference> ofind(Container auto& container, const auto& value)
Where Container::real_reference is something that matches the constness unlike reference typedef in vector, for example consider this:
using CVI = const std::vector<int>;
static_assert(std::is_same_v<int&, CVI::reference>); // compiles
note: I know I should make second argument also more constrained, but for simplicity I left it as just const auto&.
Ranges already gives us a way to get the correct reference type of any range:
std::ranges::range_reference_t<R>
If R is vector<int>, that's int&. If R is vector<int> const, that's int const&. If R is span<int> const, that's... still int& because span is shallow-const (which is something your trait gets wrong, because it assumes that everything is deep-const).
This trait isn't magical, all it does is give you precisely the type that the underlying iterator dereferences to: decltype(*ranges::begin(r)).
With that, your find can look like:
template <range R>
auto ofind(R& container, const auto& value) -> boost::optional<range_reference_t<R>>;
Note that if you actually need to use the types of your parameters, using abbreviated function template syntax is not actually going to be all that abbreviated due to having to write decltype, so you can just... use normal function template syntax.

Implementing a C++ range-for wrapper for lvalues and rvalues

I've implemented a little helper wrapper for range-for loops that allows iterating over both keys and values of the associative Qt containers like QMap and QHash, extracting each pair as a structured binding, e.g.:
const QMap<int, QString> digitMap = { {1, "one"}, {2, "two"}, {3, "three"} };
for (auto [intKey, strVal] : make_keyval(digitMap)) {
qDebug() << intKey << "->" << strVal;
}
This isn't supported out-of-the-box for the Qt containers as they require using a specific pair of constKeyValueBegin() and constKeyValueEnd() methods (let's assume only non-mutating iterations). So my idea was to write a simple wrapper type that provides a pair of regular begin() and end() methods which simply call the keyValue ones on the container.
That's easy enough, but as a stretch goal I also wanted to make the wrapper usable with temporaries, like make_keyval(sometype.toMap()). The main challenge there was extending the lifetime of the temporary through the end of the iteration, as I'm going through a proxy object. Here's the solution I came up with:
template<typename C>
struct key_value_range_iterator {
key_value_range_iterator(const C& container) : m_rvalueContainer(nullptr), m_containerRef(container) {}
key_value_range_iterator(const C&& container) : m_rvalueContainer(std::make_unique<C>(std::move(container))), m_containerRef(*m_rvalueContainer) {}
typename C::const_key_value_iterator begin() const { return m_containerRef.constKeyValueBegin(); }
typename C::const_key_value_iterator end() const { return m_containerRef.constKeyValueEnd(); }
private:
const std::unique_ptr<C> m_rvalueContainer;
const C& m_containerRef;
};
template<typename C>
auto make_keyval(C&& container) { return key_value_range_iterator(std::forward<C>(container)); }
This seems to work fine for both regular variables and temporaries. For temporaries, m_rvalueContainer is used to store the moved temporary for the duration of the iteration, then it's referenced by m_containerRef. In the regular variable case, we just store the lvalue reference in m_containerRef directly leaving m_rvalueContainer unset. I verified the right constructor gets called in each case, and that the temporary only gets destroyed after the range-for loop completes.
So my question is simply: is this a correct implementation for my wrapper, or did I miss something? Or maybe there's a corner case I haven't thought of?
Note that in my initial version, I had m_rvalueContainer as a regular value, but I figured this would end up instantiating an empty container for nothing in the lvalue case (though that's a cheap operation for Qt containers), so I replaced it with unique_ptr to ensure there's no overhead in that case. Yes, it still ends up initializing it to nullptr, but that's neglectable.
Any other comments or recommendations?
Since make_keyval knows if the object passed in is lvalue or rvalue, you can pass that parameter on to your wrapper.
#include <type_traits>
template<typename C>
struct key_value_range_iterator {
key_value_range_iterator(const C container) : m_containerRef(container) {}
using iterator = typename std::remove_reference_t<C>::const_key_value_iterator;
iterator begin() const { return m_containerRef.constKeyValueBegin(); }
iterator end() const { return m_containerRef.constKeyValueEnd(); }
private:
const C m_containerRef;
};
template<typename C>
auto make_keyval(C&& container) { return key_value_range_iterator<C>(std::forward<C>(container)); }
When passing an lvalue C is deduces as QMap& making the wrapper hold a reference.
When passing an rvalue C is deduces as QMap making the wrapper move the rvalue into it's member.
Since C can be QMap& we need to use std::remove_reference to obtain the iterator type succesfully for the lvalue case.

Forwarding the same value to two or more functions

When using forwarding references, is it a bad idea to forward the
same value to more than one function? Consider the following piece of code:
template<typename Container>
constexpr auto
front(Container&& c)
-> typename Container::value_type
{ return std::forward<Container>(c).front(); }
template<typename Container>
constexpr auto
back(Container&& c)
-> typename Container::value_type
{ return std::forward<Container>(c).back(); }
template<typename Container>
constexpr auto
get_corner(Container&& c)
{
return do_something(front(std::forward<Container(c)),
back(std::forward<Container>(c));
}
If Container is an lvalue-reference, the function works just fine. However, I'm worrying about situations where rvalues are passed on to it, because the value would get invalidated once a move occurs. My doubt is: Is there a correct way to forward the container in that case, without losing the value category?
In general, it is not reasonable for the same function to forward the same parameter twice. Not unless it has specific knowledge of what the receiver of that forwarded parameter will do.
Remember: the behavior of std::forward can be equivalent to the behavior of std::move, depending on what parameter the user passed in. And the behavior of an xvalue will be contingent on how the receiving function processes it. If the receiver takes a non-const rvalue reference, it will likely move from that value if possible. That would leave you holding a moved-from object. If it takes a value, it will certainly move from it if the type supports it.
So unless you have specific knowledge of the expected behavior of the operations you are using, it is not safe to forward a parameter more than once.
There's actually no rvalue-reference version of std::begin - we just have (setting aside constexpr and return values):
template <class C>
??? begin(C& );
template <class C>
??? begin(C const& );
For lvalue containers, you get iterator, and for rvalue containers, you get const_iterator (or whatever the container-specific equivalent ends up being).
The one real problem in your code is returning decltype(auto). For lvalue containers, that's fine - you'll return a reference to an object whose lifetime exceeds the function. But for rvalue containers, that's returning a dangling reference. You'll want to return a reference for lvalue containers and a value for rvalue containers.
On top of that, forward-ing the containers into begin()/end() is probably not what you want to do. It'd be more efficient to conditionally wrap the result of the select() as a move iterator. Something like this answer of mine:
template <typename Container,
typename V = decltype(*std::begin(std::declval<Container&>())),
typename R = std::conditional_t<
std::is_lvalue_reference<Container>::value,
V,
std::remove_reference_t<V>
>
>
constexpr R operator()(Container&& c)
{
auto it = select(std::begin(c), std::end(c));
return *make_forward_iterator<Container>(it);
}
There's probably a less verbose way to express all of that.
You presumably realize that you wouldn't want to std::move an object being passed to multiple functions:
std::string s = "hello";
std::string hello1 = std::move(s);
std::string hello2 = std::move(s); // hello2 != "hello"
The role of forward is simply to restore any rvalue status that a parameter had when it was passed to the function.
We can quickly demonstrate that it is bad practice by forwarding one parameter two times to a function that has a move effect:
#include <iostream>
#include <string>
struct S {
std::string name_ = "defaulted";
S() = default;
S(const char* name) : name_(name) {}
S(S&& rhs) { std::swap(name_, rhs.name_); name_ += " moved"; }
};
void fn(S s)
{
std::cout << "fn(" << s.name_ << ")\n";
}
template<typename T>
void fwd_test(T&& t)
{
fn(std::forward<T>(t));
fn(std::forward<T>(t));
}
int main() {
fwd_test(S("source"));
}
http://ideone.com/NRM8Ph
If forwarding was safe, we should see fn(source moved) twice, but instead we see:
fn(source moved)
fn(defaulted moved)
In general, yes, this is potentially dangerous.
Forwarding a parameter ensures that if the value received by the universal reference parameter is an rvalue of some sort, it will continue to be an rvalue when it is forwarded. If the value is ultimately forwarded to a function (such as a move-constructor) that consumes the value by moving from it, its internal state is not likely to be valid for use in subsequent calls.
If you do not forward the parameter, it will not (in general) be eligible for move operations, so you would be safe from such behavior.
In your case, front and back (both the free functions and the member functions) do not perform a move on the container, so the specific example you gave should be safe. However, this also demonstrates that there's no reason to forward the container, since an rvalue won't be given different treatment from an lvalue--which is the only reason to preserve the distinction by forwarding the value in the first place.

What's the difference between a Predicate and a Functor?

I just read somebody call a class with a constructor and an operator() a predicate:
// Example
class Foo {
public:
Foo(Bar);
bool operator()(Baz);
private:
Bar bar;
};
However, I haven't heard the word predicate being used in this context before. I would call such a thing a functor. For me, a predicate would be something from the domain of formal logic.
This raises the following questions:
Is this a common word for something like Foo?
Are both terms used interchangeably, or do they mean slightly different things?
Or
Does the return type (bool versus something else) have something to do with it?
What about the operator() being const?
Functor is a term that refers to an entity that supports operator () in expressions (with zero or more parameters), i.e. something that syntactically behaves as a function. Functor is not necessarily an object of some class with overloaded operator (). Ordinary function names are functors as well. Although in some contexts you can see the term "functor" used in a more narrow and exclusive sense: just class objects, but not ordinary functions.
A predicate is a specific kind of functor: a functor that evaluates to a boolean value. It is not necessarily a value of bool type, but rather a value of any type with "boolean" semantics. The type should be implicitly convertible to bool though.
The shown class is a functor that implements a predicate.
A predicate is a boolean function.
About the operator() being non-const here: it should ideally be const, yes.
A predicate is a special kind of function object. See this excellent column by Nicolai Josuttis. To quote:
A function object that returns a Boolean value is a predicate. That's
what almost all tutorials, books, and manuals write about predicates
of the STL. This, however, is not the whole story.
However, there is an additional requirement that is unfortunately not
mentioned in any manual or in the C++ Standard: A predicate should
always return the same result for the same value.
Or, in the language of C++: You should declare operator() as a
constant member function (and not play games with mutable or casts).
For the same reason, a copy of a predicate should have the same state
as the original.
The reason is that the STL algorithms will copy function objects around, and the copying should not affect the outcome of applying the function objects.
template<typename Arg0>
struct UnaryPredicate
:
public std::function<bool(Arg0 const&)>
{
bool operator()(Arg0 const& a0) const
{
return // your code here
}
};
template<typename Arg0, typename Arg1>
struct BinaryPredicate
:
public std::function<bool(Arg0 const&, Arg1 const&)>
{
bool operator()(Arg const& a0, Arg const& a1) const
{
return // your code here
}
};
As has been said, a predicate is just a user supplied directive to analyze something to a boolean state. So you can have the same two things doing the same thing...
struct is_odd
{
is_odd() : count(0);
bool operartor () (int i) const { ++count; return (i % 2) == 1; }
private
int count;
}
And...
bool isOdd(int i) { return (i % 2) == 1; }
So, what is so special about the functor? It maintains state if you want it to! That means that while these two lines of code do the same thing (assume v is a vector, with values 0 - 9)...
for_each(v.begin(), v.end(), isOdd); //using C-style function pointer
And...
is_odd fn = for_each(v.begin(), v.end(), is_odd); //using functor
Only with the second use case can you then call...
int calls = fn.count;
That is because for_each (as well as other STL algorithm functions) returns the functor it used at the end, so now the final state can be queried.
One other cool thing to note is that in the second case, we are using what is called an "anonymous" object.
From the MSDN:
Represents the method that defines a set of criteria and determines
whether the specified object meets those criteria.
http://msdn.microsoft.com/en-us/library/bfcke1bz.aspx