I tried to make multiplex of std::set, named NDos::set_multiplex, which can view the elements in perspective of various comparison objects. For example, a set of playing cards could be sorted with rank first and suit second, or suit first and rank second; NDos::set_multiplex enables to do this conveniently.
NDos::set_multiplex does this by inheriting multiple std::sets, one storing the elements, and the others storing the iterator to the elements.
NDos::IterComp is a Callable type that compares the elements refered by two iterators.
Here is the code:
/*...*/
namespace NDos {
template <class T, class Comp0, class... Comps> class set_multiplex :
private std::set<T, Comp0>,
private std::set<
typename std::set<T, Comp0>::iterator,
IterComp<typename std::set<T, Comp0>::iterator, Comps>
>... {
private:
typedef std::set<T, Comp0> Base0;
public:
/*...*/
using typename Base0::iterator;
using typename Base0::const_iterator;
using typename Base0::reverse_iterator;
using typename Base0::const_reverse_iterator;
#define Bases std::set<iterator, IterComp<iterator, Comps>>
/*constructors*/
// copy constructor : default
// move constructor : default
// copy assignment operator : default
// move assignment operator : default
// destructor : default
/*...*/
void clear() noexcept {
Base0::clear();
Bases::clear()...;
}
iterator insert(const T &value) {
return emplace(value);
}
iterator insert(T &&value) {
return emplace(std::move(value));
}
iterator insert(const_iterator pos, const T &value) {
return emplace_hint(pos, value);
}
iterator insert(const_iterator pos, T &&value) {
return emplace_hint(pos, std::move(value));
}
template <class InputIt> void insert(InputIt first, InputIt last) {
while (first != last)
insert(*first++);
}
void insert(std::initializer_list<T> ilist) {
insert(std::make_move_iterator(ilist.begin()), std::make_move_iterator(ilist.end()));
}
template <class... Args> iterator emplace(Args &&...args) {
iterator i0 = Base0::emplace(std::forward<Args>(args)...).first;
Bases::insert(i0)...;
return i0;
}
template <class... Args> iterator emplace_hint(const_iterator pos, Args &&...args) {
iterator i0 = Base0::emplace_hint(pos, std::forward<Args>(args)...).first;
Bases::insert(i0)...;
return i0;
}
iterator erase(iterator pos) {
Bases::erase(pos)...;
return Base0::erase(pos);
}
iterator erase(const_iterator first, const_iterator last) {
while (first != last)
erase(first++);
}
size_type erase(const T &key) {
iterator pos = find(key);
if (pos == end())
return 0;
else {
erase(pos);
return 1;
}
}
void swap(set_multiplex &other) noexcept {
Base0::swap(other);
Bases::swap(other)...;
}
/*...*/
#undef Bases
};
}
The parameter packs aren't expanded properly. G++ 6.2 reports those errors each expansion: (In function clear, emplace, emplace_hint, erase, and swap)
error: expected ';' before '...' token
error: parameter packs not expanded with '...'
Why do these happen?
In C++11 you can't simply do this:
Bases::clear()...;
The same happens for all the other places where you have used ... in that way:
Bases::insert(i0)...;
Bases::erase(pos)...;
Bases::swap(other)...;
Try to use something like this:
void clear() noexcept {
Base0::clear();
int _[] = { 0, (Bases::clear(), 0)... };
(void)_; // silent warnings, nothing more
}
That is a common trick used around while waiting for C++17 and its fold expressions.
A particular mention for swap function: if you swap other with Base0, other theoretically will contain data in Base0 after the swap. Using it once more for another swap doesn't seem to be a good idea.
Maybe you should review the implementation of your swap function.
Related
I have the following simplified code representing a range of integers that I want to use with various std algorithms. I am trying to update my code to use C++20's ranges versions of the algorithms so I can delete all of the begin() and end() calls. In the below code, std::any_of works with my container and iterator, but std::ranges::any_of does not.
#include <iostream>
#include <algorithm>
class Number_Iterator {
public:
using iterator_category = std::input_iterator_tag;
using value_type = int;
using difference_type = int;
using pointer = int*;
using reference = int&;
Number_Iterator(int start) noexcept : value(start) {}
Number_Iterator& operator++() noexcept { ++value; return *this; }
bool operator==(const Number_Iterator& other) const noexcept = default;
int operator*() const noexcept { return value; }
private:
int value;
};
class Numbers {
public:
Numbers(int begin, int end) noexcept : begin_value(begin), end_value(end) {}
Number_Iterator begin() const noexcept { return {begin_value}; }
Number_Iterator end() const noexcept { return {end_value}; }
private:
int begin_value;
int end_value;
};
int main() {
const auto set = Numbers(1, 10);
const auto multiple_of_three = [](const auto n) { return n % 3 == 0; };
// Compiles and runs correctly
if(std::any_of(set.begin(), set.end(), multiple_of_three)) {
std::cout << "Contains multiple of three.\n";
}
// Does not compile
if(std::ranges::any_of(set, multiple_of_three)) {
std::cout << "Contains multiple of three.\n";
}
return 0;
}
When I try to compile the above code, I get the following error messages from Visual Studio 2019 (16.11.15) with the flag /std:c++20:
Source.cpp(42,21): error C2672: 'operator __surrogate_func': no matching overloaded function found
Source.cpp(42,7): error C7602: 'std::ranges::_Any_of_fn::operator ()': the associated constraints are not satisfied
1>C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.29.30133\include\algorithm(1191): message : see declaration of 'std::ranges::_Any_of_fn::operator ()'
I have tried looking at the std::ranges::_Any_of_fn::operator() declaration, but I find myself more confused by that.
What am I missing to get the std::ranges algorithms to work with my container?
For the curious, what I'm actually iterating over are squares on a chess board, but those are represented by integers, so the difference from the above code isn't so great.
To use your range with any_of it must satisfy the input_range concept:
template< class T >
concept input_range =
ranges::range<T> && std::input_iterator<ranges::iterator_t<T>>;
Then via the input_iterator concept:
template<class I>
concept input_iterator =
std::input_or_output_iterator<I> &&
std::indirectly_readable<I> &&
requires { typename /*ITER_CONCEPT*/<I>; } &&
std::derived_from</*ITER_CONCEPT*/<I>, std::input_iterator_tag>;
and via the input_or_output_iterator concept
template <class I>
concept input_or_output_iterator =
requires(I i) {
{ *i } -> /*can-reference*/;
} &&
std::weakly_incrementable<I>;
you land in the weakly_incrementable concept:
template<class I>
concept weakly_incrementable =
std::movable<I> &&
requires(I i) {
typename std::iter_difference_t<I>;
requires /*is-signed-integer-like*/<std::iter_difference_t<I>>;
{ ++i } -> std::same_as<I&>; // pre-increment
i++; // post-increment
};
in which you see that the iterator must have both the pre-increment and post-increment versions of operator++.
The iterator must also be default constructible because std::ranges::end creates a sentinel:
template< class T >
requires /* ... */
constexpr std::sentinel_for<ranges::iterator_t<T>> auto end( T&& t );
And sentinel_for
template<class S, class I>
concept sentinel_for =
std::semiregular<S> &&
std::input_or_output_iterator<I> &&
__WeaklyEqualityComparableWith<S, I>;
requires it to satisfy semiregular:
template <class T>
concept semiregular = std::copyable<T> && std::default_initializable<T>;
But without being default constructible, this substitution will fail:
template < class T >
concept default_initializable = std::constructible_from<T> && requires { T{}; } && ...
Apparently, the std::ranges algorithms require two more methods in the iterator: a default constructor and a post-increment operator (return value optional). Adding these methods allows the code to compile and run correctly:
Number_Iterator() noexcept : value(-1) {}
void operator++(int) noexcept { ++value; }
I'm pretty new in C++. I have wrote a function template to sum all elements in a vector like this:
template<typename T>
inline T sumator(const std::vector<T> &sequence) {
T init = T{};
return accumulate(sequence.begin(), sequence.end(), init);
}
All work well until the default constructor is not declare, but declare constructor from, for example, 2 arguments.
Like this:
class A {
private:
int a;
int b;
public:
// A() = default;
A(int aa, int bb) : a(aa),b(bb){};
A operator+(const A &right) const {
return {a + right.a, b + right.b};
}
bool operator==(const A &right) const {
return (a == right.a && b == right.b);
}
};
vector<A> bc = {{3,5},{5,5}};
A res = Adder::sumator(works);
A result = {8,10};
assert(result,res);
Now I get an error like : error: no matching constructor for initialization of 'A'
T init = T{};
How can I avoid this situation, not using https://en.cppreference.com/w/cpp/types/is_default_constructible
UPD: I have many classes, some of them with default constructor, some without but with constructor for 2 arg, some for 3, e.t.c. And I want to accumulate every situation well
Possible solutions that I can think of.
Let users rely on std::accumulate by providing an initial vlaue.
Provide an overload of sumator with an initial value. Users can call the overload with an initial value if they have a type that does not have default constructor.
Insist that the type for which the function is invoked have a default consructor.
template<typename T>
inline T sumator(const std::vector<T> &sequence, T init) {
return accumulate(sequence.begin(), sequence.end(), init);
}
template<typename T>
inline T sumator(const std::vector<T> &sequence) {
return accumulate(sequence.begin(), sequence.end(), {});
}
You can add another parameter with default argument.
template<typename T>
inline T sumator(const std::vector<T> &sequence, const T& init = T{}) {
return accumulate(sequence.begin(), sequence.end(), init);
}
For types could be default-constructed you can still
sumator(some_vector);
And specify the default value when T can't be default-constructed. e.g.
sumator(some_vector, A{0, 0});
You can split it in two overloads and add a static_assert to provide a nice compilation error message if someone tries to use it the wrong way.
One overload would be valid only if the type is default constructible and one overload would be valid for types that is not as well as for those that are:
#include <type_traits>
#include <vector>
template<typename T>
T sumator(const std::vector<T>& sequence) {
static_assert(std::is_default_constructible_v<T>);
return std::accumulate(sequence.begin(), sequence.end(), T{});
}
template<typename T>
T sumator(const std::vector<T>& sequence, const T& init) {
return std::accumulate(sequence.begin(), sequence.end(), init);
}
Another option is to add a default argument that can be used for types that are default constructible - and you could also make it more generic to make it work with not only vectors but with lists etc.
template<template<class, class...> class C, class T, class... Ts>
T sumator(const C<T, Ts...>& sequence, const T& init = T{}) {
return std::accumulate(sequence.begin(), sequence.end(), init);
}
Is it possible the value passed to insert is left in a moved from state after move insertion if insert returns false?
#include <memory>
#include <map>
#include <cassert>
struct less
{
template< typename T >
bool operator () (const std::shared_ptr<T> & lhs, const std::shared_ptr<T> & rhs) const
{
return *lhs < *rhs;
}
};
int main() {
using key_type = int;
using value_type = int;
using map_type = std::map<std::shared_ptr<key_type>, std::shared_ptr<value_type>, less>;
map_type m;
auto p = typename map_type::value_type{std::make_shared<key_type>(1), std::make_shared<value_type>(1)};
if (!m.insert(p).second) {
assert(false);
}
assert(p.first);
assert(p.second);
if (m.insert(std::move(p)).second) {
assert(false);
}
assert(p.first);
assert(p.second);
}
Is the behavior of the last two assertion implementation defined?
From [map.modifiers/2] on std::map::insert, we have
template<class P>
pair<iterator, bool> insert(P&& x);
[...]
Effects: The first form is equivalent to return emplace(std::forward<P>(x)).
So it's in std::map::emplace... from [associative.reqmts/8] (emphasis mine):
a_uniq.emplace(args)
Effects: Inserts a value_type object t constructed with std::forward<Args>(args)... if and only if there is no element in the container with key equivalent to the key of t.
Hence, construction does not take place if there is already an object in the container that is associated with an equivalent key.
Let's verify with <map> from the Llvm implementation. In what follows, I've deleted some parts of the code to make it more readable. First, std::map::insert does this:
template <class _Pp, /* some SFINAE... */>
/* symbol visibility ... */
pair<iterator, bool> insert(_Pp&& __p)
{return __tree_.__insert_unique(_VSTD::forward<_Pp>(__p));}
Let's go to __tree::insert_unique, then:
pair<iterator, bool> __insert_unique(__container_value_type&& __v) {
return __emplace_unique_key_args(_NodeTypes::__get_key(__v), _VSTD::move(__v));
}
Still not there... but in __tree::emplace_unique_key_args it comes:
/* Template, template, template... return value... template */
__tree</* ... */>::__emplace_unique_key_args(_Key const& __k, _Args& __args)
{
__parent_pointer __parent;
__node_base_pointer& __child = __find_equal(__parent, __k);
__node_pointer __r = static_cast<__node_pointer>(__child);
bool __inserted = false;
if (__child == nullptr)
{
/* Some legacy dispatch for C++03... */
// THIS IS IT:
__node_holder __h = __construct_node(_VSTD::forward<_Args>(__args)...);
__insert_node_at(__parent, __child, static_cast<__node_base_pointer>(__h.get()));
__r = __h.release();
__inserted = true;
}
return pair<iterator, bool>(iterator(__r), __inserted);
}
I think we don't have to look into __find_equal(__parent, __k) to understand that __child == nullptr is the condition that triggers the actual insertion. In this branch, the call to __construct_node forwards the arguments, which will steal the resources managed by the std::shared_ptr<int> you passed in. The other branch simply let's the arguments untouched.
Consider that I have a std::vector.
std::vector<int> blah;
blah.push_back(1);
blah.push_back(2);
I now want to pass the vector somewhere and disallow modifying the contents of the objects its contains while still allowing to modify the container when able:
// Acceptable use:
void call_something() {
std::vector<int> blah;
blah.push_back(1);
blah.push_back(2);
// Currently, compiler error because of mismatching types
something(blah);
}
void something(std::vector<const int>& blah)
{
// Auto translates to 'const int'
for ( auto& i : blah ) {
// User cannot modify i.
std::cout << i << std::endl;
}
blah.push_back(blah.size()); // This should be acceptable
blah.emplace_back(); // This should be acceptable
return;
}
// Unacceptable use:
void something_else(const std::vector<int>& blah)
{
// Because of const vector, auto translates to 'const int'
for ( auto& i : blah ) {
std::cout << i std::endl;
}
blah.push_back(blah.size()); // This will present an unacceptable compiler error.
blah.emplace_back(); // This will present an unacceptable compiler error.
return;
}
Is there an easy way to do this?
To enable the operations you wish to allow while preventing the others, you need to take a fine-grained approach to your function's interface. For example, if your calling code were to pass const iterators (begin and end) as well as a back inserter (or custom back emplacer functor), then exactly the subset of operations you showed would be possible.
template <class Iter, class F>
void something(Iter begin, Iter end, F&& append)
{
using value_type = typename std::iterator_traits<Iter>::value_type;
std::copy(begin, end, std::ostream_iterator<value_type>(std::cout, "\n"));
append(std::distance(begin, end));
append();
return;
}
That said I don't find your examples particularly compelling. Do you have a real scenario in which you must maintain mutable elements, pass a mutable container to a function, yet treat the passed elements as immutable?
There is no easy way to do this. One way would be to wrap a vector in a type that exposes only the functionality that you want to allow. For instance
template<typename T, typename A = std::allocator<T>>
struct vector_wrap
{
using iterator = typename std::vector<T, A>::const_iterator;
using const_iterator = typename std::vector<T, A>::const_iterator;
using size_type = typename std::vector<T, A>::size_type;
vector_wrap(std::vector<T, A>& vec)
: vec_(&vec)
{}
void push_back(T const& value) { vec_->push_back(value); }
void push_back(T&& value) { vec_->push_back(std::move(value)); }
size_type size() { return vec_->size(); }
iterator begin() const { return vec_->cbegin(); }
iterator end() const { return vec_->cend(); }
private:
std::vector<T, A> *vec_;
};
Since the above implementation only stores a pointer to the vector it wraps, you'll have to ensure that the lifetime of the vector is longer than that of vector_wrap.
You'll have to modify something and something_else so that they take a vector_wrap<int> as argument. Since vector_wrap::begin and vector_wrap::end return const_iterators, you'll not be allowed to modify existing elements within the for statement.
Live demo
Hi can anyone tell me why VS2010 gives me an error with this code, I can't see what's the problem with it?
Error code : error C2679: binary '=' : no operator found which takes a right-hand operand of type 'std::vector<_Ty>' (or there is no acceptable conversion)
// Elements container
typedef std::vector<CFVFElementPtr> ElementArray;
typedef std::map<CVertexSemantic::Type, ElementArray> ElementsMap;
// Create an empty array of elements
ElementsMap::value_type::second_type allElements;
// Concatinate each std::vector found within the map
std::transform(m_elementsMap.begin(), m_elementsMap.end(),
std::insert_iterator<ElementArray>(allElements, allElements.end()),
select2nd<ElementsMap::value_type>() );
All I am trying to do is this
for (auto i = m_elementsMap.begin(); i != m_elementsMap.end(); ++i)
{
const ElementArray& elements = (*i).second;
allElements.insert(allElements.end(), elements.begin(), elements.end());
}
As a response to Pablo, I tried creating a custom iterator that accepts an array of ElementArray, but I'm now getting a bucket load of errors.
template < class Container >
class container_insert_interator
{
public:
typedef container_insert_interator<Container> this_type;
typedef Container container_type;
typedef typename Container::const_reference const_reference;
typedef typename Container::value_type valty;
explicit container_insert_interator (Container& cont, typename Container::iterator iter)
: container(&cont), iter(iter)
{ }
this_type& operator = (typename const_reference value)
{
iter = container->insert( iter, std::begin(value), std::end(value) );
++iter;
return *this;
}
this_type& operator* ()
{
return *this;
}
this_type& operator++ ()
{
return *this;
}
this_type operator++ (int)
{
return *this;
}
protected:
Container* container; // pointer to container
typename Container::iterator iter ; // iterator into container
};
The problem is ElementsMap::value_type::second_type is ElementArray. That is, you're trying to insert instances of ElementArray into an ElementArray, which really holds instances of CFVFElementPtr.
Update: Changing your declaration of operator= from
this_type& operator = (typename const_reference value)
to
template<typename OtherContainer>
this_type& operator = ( OtherContainer const& value)
almost works. Except that neither VS 2010 or GCC 4.6.1 provide the vector::insert overload that returns an iterator. You'll probably need a newer compiler if you want your ranged insert to return an iterator to replace iter.
Changing the implementation to always insert at the end, namely
container->insert( container->end(), std::begin(value), std::end(value) );
in operator= compiles fine with GCC 4.6.1 (and, of course, you can remove all references to iter in your iterator class).
You might consider using a functor that can append elements to your target array along with std::for_each to walk the map:
struct ElementArray_appender
{
ElementArray_appender( ElementArray& dest_) : dest(dest_) {
};
void operator()( ElementsMap::value_type const& map_item) {
ElementArray const& vec( map_item.second);
dest.insert( dest.end(), vec.begin(), vec.end());
};
private:
ElementArray& dest;
};
// in whatever function:
std::for_each( m_elementsMap.begin(), m_elementsMap.end(), ElementArray_appender(allElements));
I found the answer, the poblem was with my custom iterator. The correct one that works is
template < class Container >
class container_back_insert_interator
{
public:
typedef container_back_insert_interator<Container> this_type;
typedef Container container_type;
typedef typename container_type::const_reference const_reference;
typedef typename container_type::value_type valty;
explicit container_back_insert_interator (container_type& cont)
: container(&cont)
{ }
this_type& operator = (const_reference value)
{
container->insert( container->end(), std::begin(value), std::end(value) );
return *this;
}
this_type& operator* ()
{
return *this;
}
this_type& operator++ ()
{
return *this;
}
this_type operator++ (int)
{
return *this;
}
protected:
container_type* container; // pointer to container
};
template < class Container >
inline container_back_insert_interator<Container> container_back_inserter(Container& cont)
{
return container_back_insert_interator<Container>(cont);
}
However, I must warn that if you do use this in Visual Studio 2010 you will have to implement an SGI form of std::transform. The version that ships with VS2010 for some reason throws numerous errors, all of which lie within the <xutility> header.
This std::transform works just fine.