I don't understand why this code compiles and runs:
std::string s;
std::cin >> s;
// if (s == "done") { // version 1
if ("done" == s) { // version 2
std::cout << "We're done here" << std::endl;
}
Version 1 works as I would expect. The compiler sees that s is a std::string, and uses the std::string definition of == to do the comparison.
However, when comparing a variable to an explicit value, I like to use the trick of putting the explicit value first in case one day I accidentally use = instead and do an assignment instead. Hence, version 2.
Now version 2 works on my compiler (llvm on MacOS) the same way as version 1, but I'm not sure why (or if it's a reliable result). I would have though that the compiler sees "done" as a char* explicit value (literal) and says, "hey, == makes no sense on char*" and give me a compilation error message. But my compiler doesn't do that, it compiles without complaint and the code executes the same way as version 1.
What am I misunderstanding here?
The type of "done" is not char*. It is const char[5].
But in any case, it works because if you include <string>, which you must have to use std::string, then (before C++20) you include an overload for operator== of the form
template< class CharT, class Traits, class Alloc >
bool operator==( const std::basic_string<CharT,Traits,Alloc>& lhs,
const CharT* rhs );
and one of the form
template< class CharT, class Traits, class Alloc >
bool operator==( const CharT* lhs,
const std::basic_string<CharT,Traits,Alloc>& rhs );
See https://en.cppreference.com/w/cpp/string/basic_string/operator_cmp for reference.
std::string is just std::basic_string with CharT being char and Traits and Alloc being some defaults that don't matter here.
So effectively one overload accepts a const std::string& as left-hand argument and const char* as right-hand argument, and the other accepts the reverse.
const char[N] can be deduced as const char* and it is implicitly convertible to the latter for every N, so the first overload is viable for s == "done" and the second for "done" == s.
The standard library is written with these overloads specifically so that your use will work. Your approach of using the "done" == s variant makes sense, because "done" = s would indeed be an error. operator= can only be overloaded with a class type on the left-hand side.
Since C++20, the overloads look a bit different. In particular it isn't necessary to have both anymore. The compiler now automatically tries overloads matching the rewritten expression with reversed operands of == when doing overload resolution.
to an explicit value,
explicit constant
I am not sure what you mean here, but "explicit value" and "explicit constant" are not standard terminology. Probably you mean "(string) literal" instead.
and says, "hey, == makes no sense on char*"
It doesn't work that way. As soon as either side of == is a class type, the compiler has to consider that == may be overloaded to match the expression.
For the class template std::basic_string the equality operator == is overloaded in particular the following way
template<class charT, class traits, class Allocator>
bool operator==(const charT* lhs, const basic_string<charT, traits, Allocator>& rhs);
So the first argument maybe a C string literal without requiring any conversion (except the standard implicit conversion from an array type to pointer to the array element type).
Actually the operator has three overloads
template<class charT, class traits, class Allocator>
bool operator==(const basic_string<charT, traits, Allocator>& lhs,
const basic_string<charT, traits, Allocator>& rhs) noexcept;
template<class charT, class traits, class Allocator>
bool operator==(const charT* lhs, const basic_string<charT, traits, Allocator>& rhs);
and
template<class charT, class traits, class Allocator>
bool operator==(const basic_string<charT, traits, Allocator>& lhs,
const charT* rhs);
Related
A quote from the standard regarding std::basic_string_view equality comparison operators (see http://eel.is/c++draft/string.view#comparison):
[Example 1: A sample conforming implementation for operator== would be:
template<class charT, class traits>
constexpr bool operator==(basic_string_view<charT, traits> lhs,
basic_string_view<charT, traits> rhs) noexcept {
return lhs.compare(rhs) == 0;
}
template<class charT, class traits>
constexpr bool operator==(basic_string_view<charT, traits> lhs,
type_identity_t<basic_string_view<charT, traits>> rhs) noexcept {
return lhs.compare(rhs) == 0;
}
— end example]
Won't the second comparison operator be sufficient for all use cases? If the answer is no please provide the example code that will stop working (or will work differently) if the first comparison operator is removed. If the answer is yes then why does the C++ standard explicitly require the first operator to be defined?
I think this is insufficient reduction as a result of the adoption of <=> in P1614. Before that paper, there were three ==s in the example:
template<class charT, class traits>
constexpr bool operator==(basic_string_view<charT, traits> lhs,
basic_string_view<charT, traits> rhs) noexcept {
return lhs.compare(rhs) == 0;
}
template<class charT, class traits>
constexpr bool operator==(basic_string_view<charT, traits> lhs,
type_identity_t<basic_string_view<charT, traits>> rhs) noexcept {
return lhs.compare(rhs) == 0;
}
template<class charT, class traits>
constexpr bool operator==(type_identity_t<basic_string_view<charT, traits>> lhs,
basic_string_view<charT, traits> rhs) noexcept {
return lhs.compare(rhs) == 0;
}
At the time, we needed three operators. The type_identity ones handle stuff like sv == literal and literal == sv, and then you need the homogeneous one to disambiguate sv == sv.
With the <=> adoption (or, more precisely, the == changes from P1185), == becomes symmetric so you don't need both type_identity operators to handle literal == sv, just the one suffices. I was basically mechanically going through and dropping unnecessary == and != overloads, so I removed that second.
But what I did not realize is with the other one gone, we now no longer need the homogeneous comparison to disambiguate from the other two (we don't have other two anymore, just other one) - it's enough to just have the one type_identity overload.
You could open an editorial issue to remove the homogeneous one. Or not, it's just an example anyway.
I have a simple map std::map<string, my_namespace::MyType>, I am using c++11 so I replaced it with unordered_map for performance reasons. I got the following error when comparing an iterator with end().
auto cit = str_map_.find(str);
if (cit != str_map_.end()) {
...
}
In instantiation of 'bool my_namespace::operator!=(const T1&, const T2&) [with T1 = std::__detail::_Node_iterator<std::pair<const std::__cxx11::basic_string, my_namespace::MyType, false, true>; T2 = std::__detail::_Node_iterator<std::pair\ <const std::__cxx11::basic_string, my_namespace::MyType, false, true>]': no matching function ...
I debugged it down to my rather creative comparison operators for my_namespace::MyType:
template <class T>
struct MyType {
T* mt_;
};
struct MyTempClass {
std::string mtc_;
static int Compare(MyType<MyTempClass> const& lhs, MyType<MyTempClass> const& rhs) {
return lhs.mt_->mtc_.compare(rhs.mt_->mtc_);
}
static int Compare(std::string const& lhs, MyType<MyTempClass> const& rhs) {
return lhs.compare(rhs.mt_->mtc_);
}
static int Compare(MyType<MyTempClass> const& lhs, std::string const& rhs) {
return lhs.mt_->mtc_.compare(rhs);
}
};
template <class T1, class T2>
bool operator !=(T1 const& lhs, T2 const& rhs) {
int res = MyTempClass::Compare(lhs, rhs);
return (res != 0);
}
template <class T1, class T2>
bool operator ==(T1 const& lhs, T2 const& rhs) {
int res = MyTempClass::Compare(lhs, rhs);
return (res != 0);
}
static std::unordered_map<std::string, MyType<MyTempClass>> my_map;
But I am still puzzled why it did happen: the same code works fine with a plain map, and values type should not be involved in iterator comparisons?
You defined an operator!= overload that takes any type as an argument. That overload is in the same namespace as the type MyType. Therefore, it can potentially be found via ADL.
As the error message indicates, the std::unordered_map iterator used by the standard library is a class template specialization, specialized on the std::unordered_map template arguments. As such, when you compare iterators with !=, ADL is performed on the arguments and the namespaces searched by ADL also include the namespaces of type template arguments of the types of the arguments. Therefore, your operator!= overload in the namespace of MyType will also be found and participate in overload resolution.
Assuming you are using libstdc++ as standard library implementation based on the error message, you can have a look at it's implementation of the operator!= for hash table iterators and you will see that it uses a base class for these iterators, and defines the comparison operators for references to the base class objects.
As a consequence, the standard overload for the iterator comparison requires a derived-to-base reference conversion in its arguments, while your overload does not.
Therefore your overload is better and will be chosen to do the cit != str_map_.end() comparison. Your overload tries to pass the arguments MyTempClass::Compare which clearly doesn't work, because these functions don't expect std::unordered_map iterators.
The solution is not to overload operators for pairs of types that do not depend on user-defined types. Restrict your overloads to your own types:
template <class T1, class T2>
bool operator !=(MyType<T1> const& lhs, T2 const& rhs) {
int res = MyTempClass::Compare(lhs, rhs);
return (res != 0);
}
template <class T1, class T2>
bool operator !=(T1 const& lhs, MyType<T2> const& rhs) {
int res = MyTempClass::Compare(lhs, rhs);
return (res != 0);
}
(equivalently for operator==).
As far as I know, it is not forbidden to overload the operators for standard library type pairs, but I also don't think that the standard library is required to account for conflicts this generates as in your code.
With std::map the standard library implementation might have chosen a different way of implementing the iterator comparison, which made it a better fit in overload resolution or avoided that ADL finds your overload by not making the iterator a template specialized on the key/value type.
I want to define operator == for boost::any in my project. Since the arguments belong to the boost namespace, this is where argument-dependent lookup will search for it. So, the signature is:
namespace boost
{
bool operator == (const boost::any &, const boost::any &);
}
However, this generates ambiguous overload errors whenever I include a boost library that compares enums for equality, such as thread/locks.hpp — the compiler sees no reason to prefer converting the enums to int and using the built-in comparison instead of converting them to boost::any and using mine.
I can hack around this by also including any such libraries in the same file as my comparator and defining custom operators for comparing boost's enums. But there got to be a better way, right?
namespace boost {
template<class T,
typename std::enable_if<std::is_same<T, any>{}, bool>::type =true
>
bool operator == (const T& lhs, const T& rhs){
return any_equal(lhs, rhs);
}
}
This is my C++ code:
inline static void swap(std::string& a1, std::string& a2) {
std::string temp( std::move(a1));
a1 = std::move( a2 );
a2 = std::move( temp );
}
I ran this function 1000000 times and it took 78ms on average, but the std one just took 13ms. I just looked at the implementation of std::swap, I found it just the the same as mine, so why is mine so slow?
According to the standard §21.3.2.8/p1 swap [string.special] (Emphasis Mine):
template<class charT, class traits, class Allocator>
void swap(basic_string<charT, traits, Allocator>& lhs,
basic_string<charT, traits, Allocator>& rhs)
noexcept(noexcept(lhs.swap(rhs)));
1 Effects: Equivalent to: lhs.swap(rhs);
Consequently, std::swap specializes/has an overload for std::string and is equivalent as calling the member function std::basic_string::swap.
A possible implementation would be:
template<class Elem, class Traits, class Alloc>
inline
void
swap(std::basic_string<Elem, Traits, Alloc>& left,
std::basic_string<Elem, Traits, Alloc>& right) noexcept(noexcept(left.swap(right))) {
left.swap(right);
}
As for why your implementation is slower, my guess is that even if you move the one string to the another, the destructor for the temporary string will still be called. Something that is not the case in the STL compatible implementation above.
Consider the following code snippet:
#include <iostream>
int main() {
std::string str = "Hello";
const char *cstr = "Hello";
if (cstr == str) {
std::cout<<"Both are same string.";
}
return 0;
}
I am having difficulty guessing how the const char* and string comparison work:
if (cstr == str) {
As per my understanding, the overloaded operator of the left operand (cstr in this case) is called with str as the argument. Now there is no overloading of == for const char*. So how does the above comparison even work?
Had the comparison been str==cstr, I would have no issues accepting it (as == for std::string is overloaded and it accepts const char* as an argument for comparison).
Note:- I am using gcc-4.8.1 for compiling the above code.
As per my understanding, the overloaded operator of the left operand (cstr in this case) is called with str as the argument. Now there is no overloading of == for const char*.
The operator is actually for std::string, and looks conceptually similar to this:
bool operator==(const char* const lhs, const std::string& rhs);
(though it reality it is templated).
This operator of std::string is added specifically for the operation you mention (comparing a std::string with a char*, when the char* is provided as the left-hand-side operand in the comparison).
Had the comparison been str==cstr, I would have no issues accepting it (as == for std::string is overloaded and it accepts const char* as an argument for comparison).
Both are present (specifically to support this situation).
From http://en.cppreference.com/w/cpp/string/basic_string/operator_cmp.
Compare a basic_string object and null-terminated array of T
template< class CharT, class traits, class Alloc >
bool operator==(const CharT* lhs, const basic_string<CharT,Traits,Alloc>& rhs);
template< class CharT, class traits, class Alloc >
bool operator==(const basic_string<CharT,Traits,Alloc>& lhs, const CharT* rhs);
...