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);
}
}
Related
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);
I wanted to introduce the spaceship operator in our Code Base, but we are currently using global templated comparison operators like this:
template <typename L, typename R>
bool operator > (const L& l, const R& r) { return r < l; }
(operator < is usually a member)
When I now try and use operator <=> in a class, my templated operator gets chosen! Why?
In cpp reference the example implementation of the ordering operators are friends and should in every case be superior to a templated function.
Example: https://godbolt.org/z/r8qjK6q6f
std::strong_ordering has comparison operators for which one parameter is of unspecified type. It's not clear how they factor in overload resolution, although it is quite probable that they are intentionally the least viable function.
Comparison operators are defined between values of this type and literal 0. This supports the expressions a <=> b == 0 or a <=> b < 0 that can be used to convert the result of a three-way comparison operator to a boolean relationship; see std::is_eq, std::is_lt, etc.
These functions are not visible to ordinary unqualified or qualified lookup, and can only be found by argument-dependent lookup when std::strong_ordering is an associated class of the arguments.
The behavior of a program that attempts to compare a strong_ordering with anything other than the integer literal 0 is undefined.
Your comparison template is wildly too general. It is implying that anything is > comparable to anything else. Either remove it, or add a requires clause.
template <typename L, typename R>
requires requires (const L& l, const R& r) { r < l; }
bool operator > (const L& l, const R& r) { return r < l; }
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 recently found that a large project has a "using namespace std::rel_ops;" in a frequently included header file, and in the global namespace. Ouch.
Specifically, it caused an issue because these two function template declarations are ambiguous:
namespace std::rel_ops {
template <class T>
bool operator!=(const T&, const T&);
}
namespace std {
template <class... TTypes, class... UTypes>
constexpr bool operator!=(const tuple<TTypes...>&, const tuple<UTypes...>&);
}
so my attempt to use an expression similar to std::tie(a.m1, a.m2, a.m3) != std::tie(b.m1, b.m2, b.m3) was ill-formed.
So the plan is to delete the using namespace std::rel_ops;, then fix the compiler errors which result, probably by defining more specific comparison operator functions. But I want to also go through the exercise of evaluating whether it would be possible this change could change the meaning of some code hidden somewhere else in this large project, without causing a compiler error.
In what conditions, if any, could two C++ programs with and without a directive using namespace std::rel_ops; differ in behavior, given that neither is ill-formed with a required diagnostic?
I suspect it would require another comparison operator function template which is less specialized than one in std::rel_ops, and which has different effective behavior from the std::rel_ops definition. Both conditions seem quite unlikely in a real project, and even less likely considered together.
There you have one example of this which tells moreless all about this class of errors. Depending on using namespace the program returns different values.
#include <utility>
class Type {
public:
Type(int) {}
};
bool operator==(Type, Type) { return false; }
template<class T , class U>
bool operator!=(const T& lhs, const U& rhs) {
return lhs == rhs;
}
using namespace std::rel_ops;
int main() {
Type bar(1);
Type baz(1);
return bar != baz;
}
Live example
Given a pair of programs with and without using namespace std::rel_ops; which don't violate a rule requiring a diagnostic, a difference in behavior can only be due to overload resolution between a member of rel_ops and another function or function template which is a worse overload in some context with both declarations viable. The overload resolution context could be:
a binary comparison expression, like E1 != E2
an explicit call using an operator-function-id as function name, like operator!=(E1, E2)
a use of operator-function-id or &operator-function-id as an initializer for a pointer to function or reference to function, in one of the contexts listed in [over.over]/1, like static_cast<bool(*)(const std::string&, const std::string&)>
It's actually not that hard for another function or function template specialization to be a worse overload than a member of std::rel_ops. Examples include:
The other function or template specialization requires a user-defined conversion.
class A {};
bool operator==(const A&, const A&);
class B {
public:
B(A);
};
bool operator==(const B&, const B&);
bool operator!=(const B&, const B&);
void test1() {
// With using-directive, selects std::rel_ops::operator!=<A>(const A&, const A&).
// Without, selects operator!=(const B&, const B&).
A{} != A{};
}
class C {
operator int() const;
};
bool operator==(const C&, const C&);
void test2() {
// With using-directive, selects std::rel_ops::operator!=<C>.
// Without, selects the built-in != via converting both arguments to int.
C{} != C{};
The other function or template specialization requires a derived-to-base "Conversion" ([over.best.ics]/6).
class D {};
bool operator==(const D&, const D&);
bool operator!=(const D&, const D&);
class E : public D {};
bool operator==(const E&, const E&);
void test3() {
// With using-directive, selects std::rel_ops::operator!=<E>.
// Without, selects operator!=(const D&, const D&).
E{} != E{};
}
The other function or template specialization has an rvalue reference parameter type.
class F {};
bool operator==(F&&, F&&);
void test4() {
// With using-directive, selects std::rel_ops::operator!=<F>.
// Without, selects operator!=(F&&, F&&).
F{} != F{};
}
The other function is a specialization of a less-specialized function template.
namespace N1 {
class A{};
bool operator==(const A&, const A&);
template <typename T1, typename T2>
bool operator!=(const T1&, const T2&);
}
void test5() {
// With using-directive, selects std::rel_ops::operator!=<N1::A>.
// Without, selects N1::operator!=<N1::A,N1::A>.
N1::A{} != N1::A{};
}
namespace N2 {
class B{};
bool operator==(const B&, const B&);
template <typename T>
bool operator!=(T&, T&);
}
void test6() {
// With using-directive, selects std::rel_ops::operator!=<N2::B>.
// Without, selects operator!=<const N2::B>.
const N2::B b1;
const N2::B b2;
b1 != b2;
}
Other categories and examples are possible, but that more than makes the point.
As far as practical concerns, it's unlikely any of the comparison operator names declared in std::rel_ops would be implemented to give results very different from the rel_ops definition for the same type, given that the related operator< or operator== is also defined. It might make a difference if "invalid" or special values have special treatment, like how for floating point types a <= b is not equivalent to !(b < a) when at least one operand is a NaN value. But the cases involving an implicit conversion to a different type could fairly easily result in different behavior. After a derived-to-base Conversion, any information in derived data members will very likely be ignored. After a converting constructor or conversion function, the comparison is on values of an entirely different type, which supposedly somehow represent the original arguments but might not represent their full functional identities.
(So for the original motivation, I decided it's worth using a static analysis tool to find all the places in the existing code naming a member of std::rel_ops, to help check for unintended changes in meaning not caught by just compiling.)
I experts, once again while practicing online, i encountered another problem. This is regarding function template. I am able to create the template but i am not sure how to overload appropriate operator. Please advise.
Question
A function template largestOfTree to return the largest of 3 elements of the same parameterised type. To what class can the function template be applied? Write a class trainEngine with fields for name,model,mass. Overload the appropriate operator so the largestOfThree function template can be applied to three trainEngine objects.
So far ?
template<class T>
bool largestOfThree(T t1, T t2, T t3){
if(t1<t2&&t2<t3){
return true;
}else{
return false;
}
}
trainEngine
class trainEngine {
private:
string name;
string model;
string mass;
public:
friend bool operator<(trainEngine const& lhs) {
if (lhs.name<lhs.model&&lhs.model<lhs.mass){
return true;
}
};
A friend operator< is going to be non-member and should thus be binary. Moreover, you forgot the return type. You likely want:
friend bool operator<(trainEngine const& lhs, trainEngine const& rhs) {
// implementation
}
This would go where your operator< declaration is currently.
Here is a list of idiomatic signatures for operator overloads, and a more elaborate explanation of what juanchopanza mentioned in the comments. Note that the non-member operators can be defined within the class body if they are marked as friends. (They are still non-member functions if you do this.)
If you overload operator '<', you should also overload operator '>'
and you have to write return type bool also.
friend bool operator<(trainEngine const& obj1, trainEngine const& obj2)
In fact, it’s convention in most code to prefer the usage of < over > But more generally, always overload the complete set of related operators; in your case, this would probably also be ==, !=, <= and >=.
Let me note that currently your implementation only depends on the right-hand-side (which you call lhs!). It is surely not what you want.
I think you wanted something like this:
bool operator<(trainEngine const& rhs) {
if(name!=rhs.name)
return(name<rhs.name);
if(model!=rhs.model)
return (model<rhs.model);
return (mass<rhs.mass);
}
or the friend version:
//within the class
friend bool operator<(trainEngine const& lhs, trainEngine const& rhs);
//outside the class
bool operator<(trainEngine const& lhs, trainEngine const& rhs) {
if(lhs.name!=rhs.name)
return(lhs.name<rhs.name);
if(lhs.model!=rhs.model)
return (lhs.model<rhs.model);
return (lhs.mass<rhs.mass);
}