std::set deduction guides don't work as I'd expect - c++

I would expect deduction guides to correctly deduce the type in below example, yet they don't:
#include <set>
struct Foo { };
bool cmp(const Foo&, const Foo& );
std::set my_set({Foo{}, Foo{}}, cmp);
The compiler error (both gcc/clang show similar diagnostic):
In file included from /opt/compiler-explorer/gcc-9.2.0/include/c++/9.2.0/x86_64-linux-gnu/bits/c++allocator.h:33,
from /opt/compiler-explorer/gcc-9.2.0/include/c++/9.2.0/bits/allocator.h:46,
from /opt/compiler-explorer/gcc-9.2.0/include/c++/9.2.0/bits/stl_tree.h:64,
from /opt/compiler-explorer/gcc-9.2.0/include/c++/9.2.0/set:60,
from <source>:1:
/opt/compiler-explorer/gcc-9.2.0/include/c++/9.2.0/ext/new_allocator.h: In instantiation of 'class __gnu_cxx::new_allocator<bool(const Foo&, const Foo&)>':
/opt/compiler-explorer/gcc-9.2.0/include/c++/9.2.0/bits/alloc_traits.h:634:11: recursively required by substitution of 'template<class _Alloc> struct std::__is_allocator<_Alloc, std::__void_t<typename _Alloc::value_type, decltype (declval<_Alloc&>().allocate(long unsigned int{}))> > [with _Alloc = std::allocator<bool(const Foo&, const Foo&)>]'
/opt/compiler-explorer/gcc-9.2.0/include/c++/9.2.0/bits/alloc_traits.h:634:11: required by substitution of 'template<class _Alloc> using _RequireAllocator = typename std::enable_if<std::__is_allocator<_Alloc>::value, _Alloc>::type [with _Alloc = std::allocator<bool(const Foo&, const Foo&)>]'
/opt/compiler-explorer/gcc-9.2.0/include/c++/9.2.0/bits/stl_set.h:938:5: required by substitution of 'template<class _InputIterator, class _Compare, class _Allocator, class, class, class> std::set(_InputIterator, _InputIterator, _Compare, _Allocator)-> std::set<typename std::iterator_traits<_Iter>::value_type, _Compare, _Allocator> [with _InputIterator = bool (*)(const Foo&, const Foo&); _Compare = std::less<bool(const Foo&, const Foo&)>; _Allocator = std::allocator<bool(const Foo&, const Foo&)>; <template-parameter-1-4> = void; <template-parameter-1-5> = std::less<bool(const Foo&, const Foo&)>; <template-parameter-1-6> = <missing>]'
<source>:7:36: required from here
/opt/compiler-explorer/gcc-9.2.0/include/c++/9.2.0/ext/new_allocator.h:96:7: error: 'const _Tp* __gnu_cxx::new_allocator<_Tp>::address(__gnu_cxx::new_allocator<_Tp>::const_reference) const [with _Tp = bool(const Foo&, const Foo&); __gnu_cxx::new_allocator<_Tp>::const_pointer = bool (*)(const Foo&, const Foo&); __gnu_cxx::new_allocator<_Tp>::const_reference = bool (&)(const Foo&, const Foo&)]' cannot be overloaded with '_Tp* __gnu_cxx::new_allocator<_Tp>::address(__gnu_cxx::new_allocator<_Tp>::reference) const [with _Tp = bool(const Foo&, const Foo&); __gnu_cxx::new_allocator<_Tp>::pointer = bool (*)(const Foo&, const Foo&); __gnu_cxx::new_allocator<_Tp>::reference = bool (&)(const Foo&, const Foo&)]'
96 | address(const_reference __x) const _GLIBCXX_NOEXCEPT
| ^~~~~~~
/opt/compiler-explorer/gcc-9.2.0/include/c++/9.2.0/ext/new_allocator.h:92:7: note: previous declaration '_Tp* __gnu_cxx::new_allocator<_Tp>::address(__gnu_cxx::new_allocator<_Tp>::reference) const [with _Tp = bool(const Foo&, const Foo&); __gnu_cxx::new_allocator<_Tp>::pointer = bool (*)(const Foo&, const Foo&); __gnu_cxx::new_allocator<_Tp>::reference = bool (&)(const Foo&, const Foo&)]'
92 | address(reference __x) const _GLIBCXX_NOEXCEPT
| ^~~~~~~
Compiler returned: 1
Which to me seems to hint at compiler not using the constructor taking the initializer list, but instead attempting to treat it as 2-iterator version (perhaps I am not reading it correctly, of course).
What might be the cause of this? Am I misreading deduction guides or is it something else?

It is probably a compiler/library bug, as little variant works:
Being explicit about initializer_list (Demo):
std::set my_set(std::initializer_list<Foo>{Foo{}, Foo{}}, cmp);
adding allocator (Demo):
std::set my_set({Foo{}, Foo{}}, cmp, std::allocator<Foo>{});
using Functor (Demo):
struct MyComparer
{
bool operator()(const Foo&, const Foo&) const;
};
std::set my_set({Foo{}, Foo{}}, MyComparer{});
And changing library (libc++ versus libstdc++) gives different results Demo for additional allocator variant.

Related

Unusual compiler message in gcc-8.2

The following piece of code works as expected but gives an odd looking message during compilation. Not sure what to make of it, but just trying to understand what GCC is trying to convey. Is it an error or just a GCC oddity? This is not happening in gcc-10.2
#include <algorithm>
#include <array>
#include <stdexcept>
#include <optional>
template < typename T >
struct Optional : public std::optional < T >
{
using impl_t = std::optional < T >;
using impl_t::impl_t;
constexpr inline friend bool operator == (Optional const & lhs, Optional const & rhs)
{
return static_cast < impl_t const & >(lhs) == static_cast < impl_t const & >(rhs);
}
template < typename U, typename = std::enable_if_t < std::is_convertible_v < U, T > > >
constexpr inline friend bool operator == (const Optional& lhs, U const & rhs)
{
return lhs.has_value() && static_cast < impl_t const & >(lhs) == rhs;
}
};
[[maybe_unused]]
auto triggers_unusual_compile_warning()
{
constexpr std::array < Optional < int32_t >, 4 > c{{1, 2, 3, std::nullopt}};
if (std::count(c.begin(), c.end(), std::nullopt) != 1)
throw std::runtime_error("WTH!!!");
};
int main()
{
triggers_unusual_compile_warning();
}
That small program triggers the following message
/opt/compiler-explorer/gcc-8.2.0/include/c++/8.2.0/bits/predefined_ops.h: In instantiation of 'bool __gnu_cxx::__ops::_Iter_equals_val<_Value>::operator()(_Iterator) [with _Iterator = const Optional<int>*; _Value = const std::nullopt_t]':
/opt/compiler-explorer/gcc-8.2.0/include/c++/8.2.0/bits/stl_algo.h:3194:12: required from 'typename std::iterator_traits<_Iterator>::difference_type std::__count_if(_InputIterator, _InputIterator, _Predicate) [with _InputIterator = const Optional<int>*; _Predicate = __gnu_cxx::__ops::_Iter_equals_val<const std::nullopt_t>; typename std::iterator_traits<_Iterator>::difference_type = long int]'
/opt/compiler-explorer/gcc-8.2.0/include/c++/8.2.0/bits/stl_algo.h:4082:29: required from 'typename std::iterator_traits<_Iterator>::difference_type std::count(_IIter, _IIter, const _Tp&) [with _IIter = const Optional<int>*; _Tp = std::nullopt_t; typename std::iterator_traits<_Iterator>::difference_type = long int]'
<source>:29:52: required from here
/opt/compiler-explorer/gcc-8.2.0/include/c++/8.2.0/optional:1371:5: note: candidate 1: 'constexpr bool std::operator==(const std::optional<_Tp>&, std::nullopt_t) [with _Tp = int]'
operator==(const optional<_Tp>& __lhs, nullopt_t) noexcept
^~~~~~~~
<source>:12:34: note: candidate 2: 'constexpr bool operator==(const Optional<int>&, const Optional<int>&)'
constexpr inline friend bool operator == (Optional const & lhs, Optional const & rhs)
^~~~~~~~
ASM generation compiler returned: 0
In file included from <source>:4:
/opt/compiler-explorer/gcc-8.2.0/include/c++/8.2.0/bits/predefined_ops.h: In instantiation of 'bool __gnu_cxx::__ops::_Iter_equals_val<_Value>::operator()(_Iterator) [with _Iterator = const Optional<int>*; _Value = const std::nullopt_t]':
/opt/compiler-explorer/gcc-8.2.0/include/c++/8.2.0/bits/stl_algo.h:3194:12: required from 'typename std::iterator_traits<_Iterator>::difference_type std::__count_if(_InputIterator, _InputIterator, _Predicate) [with _InputIterator = const Optional<int>*; _Predicate = __gnu_cxx::__ops::_Iter_equals_val<const std::nullopt_t>; typename std::iterator_traits<_Iterator>::difference_type = long int]'
/opt/compiler-explorer/gcc-8.2.0/include/c++/8.2.0/bits/stl_algo.h:4082:29: required from 'typename std::iterator_traits<_Iterator>::difference_type std::count(_IIter, _IIter, const _Tp&) [with _IIter = const Optional<int>*; _Tp = std::nullopt_t; typename std::iterator_traits<_Iterator>::difference_type = long int]'
<source>:29:52: required from here
/opt/compiler-explorer/gcc-8.2.0/include/c++/8.2.0/optional:1371:5: note: candidate 1: 'constexpr bool std::operator==(const std::optional<_Tp>&, std::nullopt_t) [with _Tp = int]'
operator==(const optional<_Tp>& __lhs, nullopt_t) noexcept
^~~~~~~~
<source>:12:34: note: candidate 2: 'constexpr bool operator==(const Optional<int>&, const Optional<int>&)'
constexpr inline friend bool operator == (Optional const & lhs, Optional const & rhs)
^~~~~~~~
Execution build compiler returned: 0
The problem with the code is that the equality operator is ambiguous. A std::nullopt can be converted to a std::optional.
So, by instantiating a operator== when you call std::count, the compiler cannot choose between the operator== from the library and your operator== declared as
constexpr inline friend bool operator == (Optional const & lhs, Optional const & rhs)
How exactly to solve this problem depends on the details you have in mind. For example, if you add an explicit comparison with std::nullopt_t
constexpr inline friend bool operator == (Optional const & lhs, std::nullopt_t const & rhs)
{
return static_cast < impl_t const & >(lhs) == static_cast < impl_t const & >(rhs);
}
you force the compiler to choose your more-specific implementation, and the error disappears.
But of course, a good solution depends on what you have in mind.

multiset with self-defined comparison rules. when call multiSetObject.count() has compile error?

#include <iostream>
#include <set>
#include <algorithm>
class A
{
private:
int n;
public:
A(int n_):n(n_){}
friend bool operator < (const A& a1, const A& a2) //why must be friend, but cannot be member func
{
return a1.n < a2.n;
}
friend std::ostream& operator<<(std::ostream& os, const A& a)
{
os << a.n;
return os;
}
friend class MyLess;
};
class A definition is listed above
class MyLess
{
public:
bool operator()(const A& a1, const A&a2)
{
return a1.n%10 < a2.n%10;
}
};
int main()
{
std::multiset<A, MyLess> st2;
st2.insert(A(11));
st2.insert(A(2));
st2.insert(A(5));
st2.insert(A(19));
std::cout << "count: " << st2.count(A(2)) << "\n"; //compile error
}
multiset with self-defined comparison rules. when call multiSetObject.count() has compile error? why last line has compile error, if I define st2 using std::multiset<A>, then call st2.count(), then it's ok?
Also, in class A, when I overload operator<. why it must be a friend function, but cannot be member function
Compile error:
[ 50%] Building CXX object CMakeFiles/test13.dir/main.cpp.o
In file included from /usr/include/c++/5/set:60:0,
from /home/roaddb/Desktop/ReviewCPPBook/Video/test13/main.cpp:2:
/usr/include/c++/5/bits/stl_tree.h: In instantiation of ‘std::pair<std::_Rb_tree_const_iterator<_Val>, std::_Rb_tree_const_iterator<_Val> > std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::equal_range(const _Key&) const [with _Key = A; _Val = A; _KeyOfValue = std::_Identity<A>; _Compare = MyLess; _Alloc = std::allocator<A>]’:
/usr/include/c++/5/bits/stl_tree.h:2320:61: required from ‘std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::size_type std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::count(const _Key&) const [with _Key = A; _Val = A; _KeyOfValue = std::_Identity<A>; _Compare = MyLess; _Alloc = std::allocator<A>; std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::size_type = long unsigned int]’
/usr/include/c++/5/bits/stl_multiset.h:650:30: required from ‘std::multiset<_Key, _Compare, _Alloc>::size_type std::multiset<_Key, _Compare, _Alloc>::count(const key_type&) const [with _Key = A; _Compare = MyLess; _Alloc = std::allocator<A>; std::multiset<_Key, _Compare, _Alloc>::size_type = long unsigned int; std::multiset<_Key, _Compare, _Alloc>::key_type = A]’
/home/roaddb/Desktop/ReviewCPPBook/Video/test13/main.cpp:78:45: required from here
/usr/include/c++/5/bits/stl_tree.h:1727:4: error: no match for call to ‘(const MyLess) (const A&, const A&)’
if (_M_impl._M_key_compare(_S_key(__x), __k))
^
/home/roaddb/Desktop/ReviewCPPBook/Video/test13/main.cpp:38:10: note: candidate: bool MyLess::operator()(const A&, const A&) <near match>
bool operator()(const A& a1, const A&a2)
^
/home/roaddb/Desktop/ReviewCPPBook/Video/test13/main.cpp:38:10: note: passing ‘const MyLess*’ as ‘this’ argument discards qualifiers
In file included from /usr/include/c++/5/set:60:0,
from /home/roaddb/Desktop/ReviewCPPBook/Video/test13/main.cpp:2:
/usr/include/c++/5/bits/stl_tree.h:1729:9: error: no match for call to ‘(const MyLess) (const A&, const A&)’
else if (_M_impl._M_key_compare(__k, _S_key(__x)))
^
/home/roaddb/Desktop/ReviewCPPBook/Video/test13/main.cpp:38:10: note: candidate: bool MyLess::operator()(const A&, const A&) <near match>
bool operator()(const A& a1, const A&a2)
^
/home/roaddb/Desktop/ReviewCPPBook/Video/test13/main.cpp:38:10: note: passing ‘const MyLess*’ as ‘this’ argument discards qualifiers
The big hint is in
note: passing ‘const MyLess*’ as ‘this’ argument discards qualifiers
multiset is going to provide a const MyLess, so the Function call operator must be const so that it will be compatible with a const this.
Use
bool operator()(const A& a1, const A&a2) const
^ added
instead of
bool operator()(const A& a1, const A&a2)

const vector reference arguments in c++

I consider myself fairly knowledgeable regarding the more basic aspects of C++, but sometimes I tend to get confused. I'm trying to implement a pair of functions that act on instances of std::vector and do some operations with their values, without changing them in-place. Naturally, I thought that a pass by const& would be a reasonable thing to do. However, the following MWE fails to compile, and I would like to understand why:
#include<vector>
void func1(const std::vector<const char* const>& v){
// do stuff
}
void func2(const std::vector<const std::vector<const char* const> >& v){
for(int i=0;i<v.size();++i){
func1(v[i]);
}
}
int main(){
return 0;
}
The error message looks somewhat like this:
In file included from...include/c++/4.9.3/x86_64-unknown-linux-gnu/bits/c++allocator.h:33:0,
from...include/c++/4.9.3/bits/allocator.h:46,
from...include/c++/4.9.3/vector:61,
from test.cxx:2:
.../ext/new_allocator.h: In instantiation of ‘class __gnu_cxx::new_allocator<const std::vector<const char* const> >’:
.../bits/allocator.h:92:11: required from ‘class std::allocator<const std::vector<const char* const> >’
.../ext/alloc_traits.h:172:53: required from ‘struct __gnu_cxx::__alloc_traits<std::allocator<const std::vector<const char* const> > >’
.../bits/stl_vector.h:75:28: required from ‘struct std::_Vector_base<const std::vector<const char* const>, std::allocator<const std::vector<const char* const> > >’
.../bits/stl_vector.h:214:11: required from ‘class std::vector<const std::vector<const char* const> >’
test.cxx:9:18: required from here
.../ext/new_allocator.h:93:7: error: ‘const _Tp* __gnu_cxx::new_allocator<_Tp>::address(__gnu_cxx::new_allocator<_Tp>::const_reference) const [with _Tp = const std::vector<const char* const>; __gnu_cxx::new_allocator<_Tp>::const_pointer = const std::vector<const char* const>*; __gnu_cxx::new_allocator<_Tp>::const_reference = const std::vector<const char* const>&]’ cannot be overloaded
address(const_reference __x) const _GLIBCXX_NOEXCEPT
^
.../ext/new_allocator.h:89:7: error: with ‘_Tp* __gnu_cxx::new_allocator<_Tp>::address(__gnu_cxx::new_allocator<_Tp>::reference) const [with _Tp = const std::vector<const char* const>; __gnu_cxx::new_allocator<_Tp>::pointer = const std::vector<const char* const>*; __gnu_cxx::new_allocator<_Tp>::reference = const std::vector<const char* const>&]’
address(reference __x) const _GLIBCXX_NOEXCEPT
^
.../ext/new_allocator.h: In instantiation of ‘class __gnu_cxx::new_allocator<const char* const>’:
.../bits/allocator.h:92:11: required from ‘class std::allocator<const char* const>’
.../ext/alloc_traits.h:172:53: required from ‘struct __gnu_cxx::__alloc_traits<std::allocator<const char* const> >’
.../bits/stl_vector.h:75:28: required from ‘struct std::_Vector_base<const char* const, std::allocator<const char* const> >’
.../bits/stl_vector.h:214:11: required from ‘class std::vector<const char* const>’
test.cxx:10:14: required from here
.../ext/new_allocator.h:93:7: error: ‘const _Tp* __gnu_cxx::new_allocator<_Tp>::address(__gnu_cxx::new_allocator<_Tp>::const_reference) const [with _Tp = const char* const; __gnu_cxx::new_allocator<_Tp>::const_pointer = const char* const*; __gnu_cxx::new_allocator<_Tp>::const_reference = const char* const&]’ cannot be overloaded
address(const_reference __x) const _GLIBCXX_NOEXCEPT
^
.../ext/new_allocator.h:89:7: error: with ‘_Tp* __gnu_cxx::new_allocator<_Tp>::address(__gnu_cxx::new_allocator<_Tp>::reference) const [with _Tp = const char* const; __gnu_cxx::new_allocator<_Tp>::pointer = const char* const*; __gnu_cxx::new_allocator<_Tp>::reference = const char* const&]’
address(reference __x) const _GLIBCXX_NOEXCEPT
I'm confused by the fact that the error message claims that something cannot be overloaded, referring to the call of func1 inside func2. It seems like there's an issue with resolving the const reference to the vector entry, but I really don't understand why this piece of code does not compile. I'm happy for any explanation, suggestion, or pointer to documentation about this type of behavior.
std::vector<const char* const>
You are trying to instantiate a vector with a const type. The type needs to be at least moveable, and that isn't. Change to:
std::vector<const char*>
instead
The compiler reports its problem correctly, if you just know beforehand what that means.
The std::allocator has two overloads for address.
address(reference __x)
address(const_reference __x)
The problem for the compiler is that for a template type const T, the two types T& and const T& will be the same type. And then it believes that there are two copies of the same overload.

Errors risen on std::find over a vector of objects

In this function I would like to search 'vertex' in the vector queue.
bool PriorityQueue::contains(VertexPriority vertex) const {
return (std::find(queue.begin(), queue.end(), vertex) != queue.end());
}
The vector queue is an instance of this object:
std::vector<VertexPriority> queue;
And my operator overloading is this:
bool operator==(const VertexPriority& v){ return (v.vertex == vertex); }
How can I solve this error?
The errors risen are the next ones, and at the beginning of every error there is the following path:
C:\Dev-Cpp\include\c++\3.4.2\bits\stl_algo.h
In function `_RandomAccessIterator std::find(_RandomAccessIterator, _RandomAccessIterator, const _Tp&, std::random_access_iterator_tag) [with _RandomAccessIterator = __gnu_cxx::__normal_iterator > >, _Tp = VertexPriority]':
instantiated from `_InputIterator std::find(_InputIterator, _InputIterator, const _Tp&) [with _InputIterator = __gnu_cxx::__normal_iterator > >, _Tp = VertexPriority]'
passing const VertexPriority' asthis' argument of `bool VertexPriority::operator==(const VertexPriority&)' discards qualifiers
Make the operator const:
bool operator==(const VertexPriority& v) const
{
return (v.vertex == vertex);
}
Note that this makes the member function const itself, which enables the compiler to invoke it on a const object of VertexPriority type.
This is actually precisely what the compiler sais:
passing const VertexPriority' as this' argument of `bool VertexPriority::operator==(const VertexPriority&)' discards qualifiers
The VertexPriority object that is const, is being passed as the implicit thisargument to member functions.

typename in dependent scope

Below is a condensed version of my code that gives me a compiler error. The compiler tells me to put typename in front of 'std::deque::reverse_iterator', which makes sense. But if I do I receive the error at the bottom. What does it mean? How can it be resolved?
#include <iostream>
#include <deque>
template<class T>
class Stack{
public:
Stack(){}
~Stack(){}
void push(T c) { s.push_back(c); }
void inspect() const{
for(typename std::deque<T>::reverse_iterator i=s.rbegin(); i!=s.rend(); i++)
std::cout << *i << std::endl;
}
private:
typename std::deque<T> s;
};
int main(){
Stack<int> s;
s.push(1);
s.inspect();
return 0;
}
Error:
error: no matching function for call to 'std::_Deque_iterator<int, int&, int*>::_Deque_iterator(std::reverse_iterator<std::_Deque_iterator<int, const int&, const int*> >::iterator_type)'|
note: candidates are:|
note: std::_Deque_iterator<_Tp, _Ref, _Ptr>::_Deque_iterator(const iterator&) [with _Tp = int; _Ref = int&; _Ptr = int*; std::_Deque_iterator<_Tp, _Ref, _Ptr>::iterator = std::_Deque_iterator<int, int&, int*>]|
note: no known conversion for argument 1 from 'std::reverse_iterator<std::_Deque_iterator<int, const int&, const int*> >::iterator_type {aka std::_Deque_iterator<int, const int&, const int*>}' to 'const iterator& {aka const std::_Deque_iterator<int, int&, int*>&}'|
note: std::_Deque_iterator<_Tp, _Ref, _Ptr>::_Deque_iterator() [with _Tp = int; _Ref = int&; _Ptr = int*]|
note: candidate expects 0 arguments, 1 provided|
note: std::_Deque_iterator<_Tp, _Ref, _Ptr>::_Deque_iterator(_Tp*, std::_Deque_iterator<_Tp, _Ref, _Ptr>::_Map_pointer) [with _Tp = int; _Ref = int&; _Ptr = int*; std::_Deque_iterator<_Tp, _Ref, _Ptr>::_Map_pointer = int**]|
note: candidate expects 2 arguments, 1 provided|
There's nothing dependent about std::deque<T>, so there mustn't be a typename. Only things to the right of a :: where the left depends on a template parameter is dependent.
This is a good example of where using auto would help you more than just saving typing. You're in a const member function, but trying to use your data member's reverse_iterator, not const_reverse_iterator.
Change typename std::deque<T>::reverse_iterator to typename std::deque<T>::const_reverse_iterator, or, more simply, auto.
This is in addition to the extra typename on your data member.