I've been racking my brain on this one for a few months on and off. I have defined a set of concepts which loosely describe containers and predicates.
In general, I'm looping over the container using an old-fashioned for loop and comparing equality of a parameter iterator to the end(container) iterator. What I can't figure out is why this won't compile when given a Container<T> such that T does not have an equality comparer (T::operator==(T other))
These concepts are by no means complete, and are simply for learning purposes, there are no doubt other ways to accomplish what I'm doing.
The problem code is in this short function defined in "queryalgorithms.h":
#pragma once
#include <algorithm>
#include "concepts.h"
template<typename C, typename P, typename V = typename C::value_type, typename I = typename C::iterator>
I last(C & collection, I iterator, P predicate) requires Container<C> && Predicate<P, V> && Incrementable<I> {
I out;
for (iterator; iterator != end(collection); iterator++) {
}
return out;
}
The test code which fails is here:
#include <iostream>
#include <vector>
#include <algorithm>
#include <forward_list>
#include "queryalgorithms.h"
using namespace std;
class MyClass {
public:
int value;
MyClass(int v): value(v) {};
};
template <typename T>
void Test() {
auto collection = T();
for (auto i = 0; i < 10; i++)
last(collection, begin(collection), [] (auto x) { return true; });
}
int main(int argc, char * argv[]) {
Test<vector<MyClass>>();
return 0;
}
Not sure if this is much help, but for completeness the concepts.h file is here. It's a lot and I don't feel it adds much value so I put that on pastebin.
As described above the small example only compiles for types which have an equality comparer defined (e.g. Test<vector<int>>() will work fine). And the compiler error from g++ is as follows:
/usr/include/c++/6.3.1/bits/stl_algobase.h:800:22: error: no match
for ‘operator==’ (operand types are ‘const MyClass’ and ‘const MyClass’)
if (!(*__first1 == *__first2))
So my question is, why is the compiler deducing the types of iterator != end(collection) to be the value type V rather than the iterator type I?
Edit: I'm compiling using g++ -fconcepts --std=c++17 -o test test.cc
Container<T> requires EqualityComparable<T> which requires a == b for lvalues a and b of type T. The C++ standard says that a == b for a container requires its value type to be EqualityComparable (N4659 [container.requirements.general] Table 83).
Checking Container<vector<MyClass>> in the requires clause on last instantiates vector's == operator, which is most likely implemented in terms of std::equal, which I speculate is the algorithm being defined on line 800 of stl_algobase.h.
Since MyClass is not EqualityComparable, vector<MyClass> does not model Container. The standard notably does not require vector's (or any container's) operator== to be constrained, so you get a hard error checking the constraints instead of last SFINAE-ing away and removing itself from overload resolution.
Related
I am currently making a reverse map class bidir_map. The code can be compiled using GCC in the terminal so the types and members should be in scope but Intellisense is not giving autosuggestion for specific parts of the code e.g. std::pair<K,V> members (but it does give suggestions to std::map members). What is the reason for this and is there any way to make Intellisense to give suggestions on the std::pair<K,V>'s inner structure through std::map?
#ifndef BIDIR_MAP_H
#define BIDIR_MAP_H
#include <map>
#include <algorithm>
#include <iterator>
#include <utility> // std::pair
#include <exception>
using std::map;
template <typename K, typename V>
class bidir_map : public map<K,V> {
using map<K,V>::map;
using map<K,V>::at;
public:
const K& at(const V v) const {
auto it = this->lookup(this->cbegin(), v); // <-- vscode displays `<unknown> bidir_map<K, V>::cbegin`
if (it != this->cend()) {
return it->first;
} else {
throw std::out_of_range("value not found");
}
}
private:
using map_t = typename map<K,V>;
using iter_t = typename map_t::const_iterator;
iter_t lookup(iter_t first, const V &v) const {
using val_t = typename map_t::value_type;
return std::find_if(first, this->cend(),
[&](const val_t e){ return e.second == v; }); // <-- e doesn't give autosuggestion for second
}
};
#endif
IntelliSense is unable to give names of members which are dependent on template parameter types. Example: A certain member function might exist for most template parameter types but for certain types the class might have a specialization that has no such function defined. Since we haven't provided any concrete types yet IntelliSense is unable to know beforehand if such function name is available.
A way to get by is to give the template concrete types temporarily. Visual Code 2019 seems to have a Template IntelliSense which allows you to switch out the template parameter types for concrete ones. Unfortunately Visual Studio Code does not currently have this feature.
https://godbolt.org/z/P97MaK
I'm playing with concepts and expected std::is_equality_comparable to work for vector but it does not.
#include <type_traits>
#include <vector>
#include <concepts>
struct X {};
template<std::equality_comparable T>
bool foo( T v){
return v == v;
}
int main()
{
foo(10);
foo(std::vector<X>{});
}
the compile error fails inside foo rather than at the function boundary protected by the concept.
Is this a bug or expected behaviour?
A concepts only checks that the declaration is well formed, it doesn't check the definition.
The comparison operators for std::vector are not SFINAE friendly, i.e. they are unconditionally declared, meaning that operator==(vector<T>, vector<T>) always exists, even if operator==(T, T) doesn't exists. That's why equality_comparable<std::vector<T>> is always satisfied and you get an error on v == v inside the function.
For it to work properly the vector comparison operators should be constrained, i.e.:
template< class T, class Alloc >
requires std::equality_comparable<T>
constexpr ret_type operator==( const std::vector<T,Alloc>& lhs,
const std::vector<T,Alloc>& rhs );
I have a std::set<std::unique_ptr<T>> and I'd like to move it to a std::vector<std::unique_ptr<T>>
#include <set>
#include <vector>
#include <memory>
class C {};
int main()
{
std::set<std::unique_ptr<const C>> s;
std::vector<std::unique_ptr<const C>> v;
std::move(s.begin(), s.end(), std::back_inserter(v));
}
This gives the following error on VS2017:
error C2280: 'std::unique_ptr>::unique_ptr(const std::unique_ptr<_Ty,std::default_delete<_Ty>> &)': attempting to reference a deleted function
Can't we make move iterators to non-const variables from a std::set? What could be a viable solution to this problem?
In order to extract move-only elements from a set, the only possibility is to use the extract method, which was added in C++17:
while (!s.empty())
v.emplace_back(std::move(s.extract(s.begin()).value()));
If you cannot use C++17, it is permissible to modify an element of a set (e.g. using mutable) only if you ensure that it retains the same position in the imposed ordering - that is, as long as it has the same result under your comparator when compared to all other members of the set. You can do this by providing a comparator that orders empty unique pointers before non-empty (note that the standard does not guarantee this) and erasing the modified element immediately after modifying it:
template<class T> struct MutableWrapper { mutable T value; };
template<class T> struct MutableWrapperCompare {
bool operator()(MutableWrapper<T> const& lhs, MutableWrapper<T> const& rhs) {
return lhs.value && rhs.value ? lhs.value < rhs.value : rhs.value;
}
};
int main()
{
std::set<MutableWrapper<std::unique_ptr<const C>>, MutableWrapperCompare<std::unique_ptr<const C>>> s;
std::vector<std::unique_ptr<const C>> v;
while (!s.empty())
{
v.emplace_back(std::move(s.begin()->value));
s.erase(s.begin());
}
}
This is however rather ugly and dangerous; you would be better off using boost::container::set from Boost.Container, which has the C++17 extract method (since 1.62.0; it was undocumented, but this is just an oversight, note the corresponding extract methods are documented for map and multimap).
I have a piece of code to use the RTTI as case selector
#include <iostream>
#include <vector>
#include <typeinfo>
#include <complex>
#include <algorithm>
using namespace std;
typedef complex<int> data;
typedef std::vector< data > List;
template <typename T> void zeros(T &v)
{
if (typeid(typename T::value_type)==typeid(complex<int>))
std::fill(v.begin(), v.end(), complex<int>(0, 0));
else std::fill(v.begin(), v.end(), static_cast<typename T::value_type>(0));
}
int main(int argc, char *argv[])
{
List A;
zeros(A);
return 0;
}
In above code, I use typename T::value_type to determine the data type of the list, if there is complex then I will fill the list with complex zero. Otherwise, make it zero of the corresponding type. In above code, if I set data to be int, it does compile and run without any problem. But if I set it another other type, it fail to compile. I wonder if there is any way to make above code work for different type of data (for float, double, long, int, complex, complex and complex).
p.s. the reason I want to fill the vector in two cases because I am going to create my own complex for some reason and I have to tell the code how to create the complex zero or real zero. But for testing, I can't even make it work with built-in datatype
It seems that you want both a default initialized complex (0, 0), and a zero initialized int. You can achieve this using value initialization:
template <typename T> void zeros(T &v)
{
std::fill(v.begin(), v.end(), typename T::value_type{});
}
If you really want to perform some branching depending on what is T::value_type, then it would be better to use some type trait, like the following one:
template<typename T>
struct default_value {
static const T value;
};
template<>
struct default_value<std::complex<int>> { // assuming you'll use other complex type
static const std::complex<int> value;
};
template<typename T>
const T default_value<T>::value = T{};
const std::complex<int> default_value<std::complex<int>>::value = std::complex<int>(0, 0);
// now use it
template <typename T> void zeros(T &v)
{
std::fill(v.begin(), v.end(), default_value<typename T::value_type>::value);
}
But again, default construction and value initialization for built in types should be enough.
I'm trying to use an Incrementable type with a boost::counting_iterator.
The boost::counting_iterator documentation says that the iterator works for Incrementable types, i.e. types that are CopyConstructible, Assignable, PreIncrementable, and EqualityComparable.
My Incrementable type:
template<class T> struct Incrementable {
// CopyConstructible:
Incrementable() : value(0) {}
Incrementable(const Incrementable& other) : value(other.value) {}
explicit Incrementable(const T& other) : value(other) {}
// Assignable:
inline Incrementable& operator=(const Incrementable& other) {
value = other.value;
return *this;
}
// PreIncrementable:
inline Incrementable& operator++() {
++value;
return *this;
}
// EqualityComparable:
friend
inline bool operator==(const Incrementable& a, const Incrementable& b) {
return a.value == b.value;
}
T value;
};
This fails to compile:
#include <boost/iterator/counting_iterator.hpp>
#include "incrementable.h"
int main() {
boost::counting_iterator<Incrementable<int>> a(Incrementable<int>(0));
return 0;
}
The error:
usr/local/include/boost/iterator/iterator_categories.hpp:161:60: error: no type named 'iterator_category' in 'boost::detail::iterator_traits<Incrementable<int> >'
typename boost::detail::iterator_traits<Iterator>::iterator_category
I'd guess that I need to implement an iterator_category either for:
a counting_iterator of my Incrementable type,
or as the error says, for my Incrementable type (does this even make sense? my Incrementable type is not an iterator).
Neither is clear from the documentation (which omits this topic completely), and I fail to find any information about it in the other parts of the library.
So I added the following to boost::detail namespace:
namespace boost { namespace detail {
template <class T> struct is_numeric<Incrementable<T>>
: mpl::true_ {};
}} // boost::detail namespace
and now everything compiles and works as expected. Still, I really doubt that the library was intended to be used this way.
Anyone knows the proper/clean way to implement this?
Steve Jessop's suggestion: specializing std::numeric_limits also works:
namespace std {
template<class T>
class numeric_limits<Incrementable<T>> : public numeric_limits<T> {
public:
static const bool is_specialized = true;
};
}
Still I don't know if this is the right thing to do for an incrementable type.
I'm not sure whether Boost defines "incrementable type" as you say. If it does define it as you say, then there's a documentation bug, it shouldn't say that counting_iterator works for "any incrementable type", because those aren't the whole requirements. Or I suppose it's true if "provided that you specify the other template parameters correctly" goes without saying.
The requirements on the Incrementable template parameter for counting_iterator are given in the document you link to (starting from "iterator_category is defined as follows...", because the actual requirements section refers back to iterator_category).
You should not specialize boost::detail::is_numeric. You should specialize std::numeric_limits. But in your example, I think you've actually narrowed the interface of T too much to claim that your type is numeric (it doesn't have arithmetic). If your type is neither numeric nor an iterator, I think you're supposed to specify CategoryOrTraversal as forward_iterator_tag. I may have missed something.