Could removing "using namespace std::rel_ops" change behavior? - c++

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.)

Related

unexpected error in unordered_map iterator comparison

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.

Why standard containers use function templates instead of non-template Koenig operators

This question is inspired by Issue with std::reference_wrapper. Let' say, for example, operator< for std::vector. It's defined as a function template as
template< class T, class Alloc >
bool operator<( const vector<T,Alloc>& lhs,
const vector<T,Alloc>& rhs );
As a result, implicit conversion of function argument to the type of the corresponding function parameter is denied (basically because of its template nature). This greatly reduces the usefulness and convenience of std::reference_wrapper. For example, you cannot use std::sort on std::vector<std::reference_wrapper<std::vector<int>>>.
On the other hand, all the problems are solved only if operator< is defined as a non-template Koenig operator like
template <...>
class vector ... {
friend bool operator<(const vector& a, const vector& b) {...}
};
I'm wondering why the standard library has adopted the former approach instead of this?
Consider this code (A.h):
template <class T>
class A {
public:
T m_x;
friend bool operator<(const A & lhs, const A & rhs) {
return lhs.m_x < rhs.m_x;
}
};
And main.cpp:
#include "A.h"
namespace buddy {
bool operator<(const A<double> & lhs, const A<double> &rhs) {
return lhs.m_x > rhs.m_x;
};
}
using namespace buddy;
int main(int argc, char ** argv) {
A<double> a1;
A<double> a2;
a1 < a2;
return 0;
}
This code does not compile:
main.cpp:14:5: error: ambiguous overload for ‘operator<’ (operand types are ‘A’ and ‘A’)
a1 < a2;
The reason of course is that both of the operator<'s are exact matches. On the other hand, if we change the first operator< to (defined outside the class):
template <class T>
bool operator<(const A<T> & lhs, const A<T> & rhs) {
return lhs.m_x < rhs.m_x;
}
The compiler stops complaining: it's now a competition between an exact match, and a function template, so the exact match is used.
If operator< was defined in the fashion that you're suggesting, there would be no reasonable way for users of std::vector to redefine the behavior of operator<, short of specializing std::vector themselves, which is a lot more work.
In conclusion, the standard writers elected to make it easier to overload operator<, than to provide an operator< that might be more useful in certain situations. I think they made the right choice.

SFINAE with boost enable if

I am trying to implement a templated pair class that uses sfinae to distinguish between array and non-array types. So far, I have the following code:
template <typename T>
class pair
{
public:
//default constructors. one for non-array types and one for arrays. These work.
template <typename temp_type = T>
pair(typename boost::disable_if<boost::is_array<temp_type>>::type* ignore = 0)
: first(T())
, second(T())
{}
template <typename temp_type = T>
pair(typename boost::enable_if<boost::is_array<temp_type>>::type* ignore = 0)
{}
//assignment operator attempts for non-array types (not in code at same time.) These don't work.
template<typename temp_type = T>
pair<temp_type>& operator=(pair<typename boost::disable_if<boost::is_array<temp_type>>::type> const& rhs)
{
this->first = rhs.first;
this->second = rhs.second;
return *this;
}
template<typename temp_type = T>
auto operator=(pair<temp_type> const& rhs) -> pair<typename boost::disable_if<boost::is_array<temp_type>>::type>&
{
this->first = rhs.first;
this->second = rhs.second;
return *this;
}
T first;
T second;
};
The first attempt at the assignment operator fails with an "illegal use of type void" error. The second compiles, but when I debug, MSVC tells me that "no executable code is associated with that line." I am using the MSVC 12 64 bit compiler.
If you could offer any insight as to what is wrong here, that would be very helpful.
Also, just a few observations I've made about sfinae in c++ (that may be right or wrong):
sfinae requires the member function to be templated but it cannot use the class template type(s) for this purpose. Why?
sfinae can be done by only modifying the template parameters and not the return type or inputs. How, exactly does this work. What's the flexibility of this method?
I know this is long winded and references topics covered in numerous other posts, but I haven't been able to put those explanations together in a way that concisely explains sfinae in c++.
Some posts I've read:
Explain C++ SFINAE to a non-C++ programmer (high level intro)
Select class constructor using enable_if (sfinae for constructors)
Thanks for any help.
EDIT:
I have modified my code based on the comments below and I still can't get this to work as expected (The compiler is not even seeing the source.) It's an interesting situation because the example is completely contrived and the default assignment operator actually works in this scenario. Nevertheless, I don't think the compiler should be overriding my attempt to overload the operator.
I have tried the following 4 methods and none of them seem to be built into the executable:
template<typename temp_type = T>
pair<temp_type>& operator=(pair<typename boost::disable_if<boost::is_array<temp_type>, temp_type>::type> const& rhs)
{
this->first = rhs.first;
this->second = rhs.second;
return *this;
}
template<typename temp_type = T>
auto operator=(pair<temp_type> const& rhs) -> pair<typename boost::disable_if<boost::is_array<temp_type>, temp_type>::type>&
{
this->first = rhs.first;
this->second = rhs.second;
return *this;
}
template<typename ret_type = boost::disable_if<boost::is_array<T>, T>::type, typename = void>
pair<ret_type>& operator=(pair<ret_type> const& rhs)
{
this->first = rhs.first;
this->second = rhs.second;
return *this;
}
template<typename temp_type = T, typename boost::enable_if<boost::is_array<temp_type>, temp_type>::type = 0>
pair<T>& operator=(pair<temp_type> const& rhs)
{
this->first = rhs.first;
this->second = rhs.second;
return *this;
}
Thoughts?
Don't forget, converting assignment can be implemented by template functions, but copy and move assignment operators can't.
A user-declared copy assignment operator X::operator= is a non-static non-template member function of class X with exactly one parameter of type X, X&, const X&, volatile X& or const volatile X&.
If the class definition does not explicitly declare a copy assignment operator, one is declared implicitly. If the class definition declares a move constructor or move assignment operator, the implicitly declared copy assignment operator is defined as deleted; otherwise, it is defined as defaulted
A user-declared move assignment operator X::operator= is a non-static non-template member function of class X with exactly one parameter of type X&&, const X&&, volatile X&&, or const volatile X&&.
and the note
Because a template assignment operator or an assignment operator taking an rvalue reference parameter is never a copy assignment operator, the presence of such an assignment operator does not suppress the implicit declaration of a copy assignment operator. Such assignment operators participate in overload resolution with other assignment operators, including copy assignment operators, and, if selected, will be used to assign an object.
(above quotes found in section 12.8, wording from draft n3936)
Why your attempts fail to work is explained in #BenVoigt 's answer, here is only an alternative solution. You can dispatch the operator= call to an appropriate overload depending on the type your template was instantiated with:
#include <iostream>
#include <boost/type_traits.hpp>
template <typename T>
class pair
{
public:
pair& operator=(pair const& rhs)
{
return assign(rhs, boost::is_array<T>());
}
private:
pair& assign(pair const&, boost::true_type)
{
std::cout << "array" << std::endl;
return *this;
}
pair& assign(pair const&, boost::false_type)
{
std::cout << "not array" << std::endl;
return *this;
}
};
int main()
{
pair<int> a, b;
b = a;
pair<int[]> c, d;
c = d;
}
Output:
not array
array
And you can do the same with constructors, delegating (C++11) the call to another one:
pair() : pair(boost::is_array<T>()) {}
pair(boost::true_type) { /*initialize array pair*/ }
pair(boost::false_type) { /*initialize non-array pair*/ }
Code looks cleaner and you don't have to compete with the compiler on whose operator= better matches the actual argument.
DEMO

What's the syntax to overload operator== as a free function with templated parameters?

I have a set of polymorphic classes, such as:
class Apple {};
class Red : public Apple {};
class Green : public Apple {};
And free functions which compare them:
bool operator==(const Apple&, const Apple&);
bool operator< (const Apple&, const Apple&);
I'm designing a copyable wrapper class which will allow me to use classes Red and Green as keys in STL maps while retaining their polymorphic behaviour.
template<typename Cat>
class Copy
{
public:
Copy(const Cat& inCat) : type(inCat.clone()) {}
~Copy() { delete type; }
Cat* operator->() { return type; }
Cat& operator*() { return *type; }
private:
Copy() : type(0) {}
Cat* type;
};
I want the Copy<Apples> type to be as interchangeable with Apples as possible. There are a few more functions I'll have to add to the Copy class above, but for now I'm working on a free function for operator==, as follows:
template<typename Cat>
bool operator==(const Copy<Cat>& copy, const Cat& e) {
return *copy == e;
}
Here is part of my testing code:
Red red;
Copy<Apple> redCopy = red;
Copy<Apple> redCopy2 = redCopy;
assert(redCopy == Red());
But the compiler is telling me
../src/main.cpp:91: error: no match for ‘operator==’ in ‘redCopy == Red()’
How do I get it to recognize my operator== above? I suspect the answer might be in adding some implicit conversion code somewhere but I'm not sure what to do.
Your template is declared as
template <typename Cat>
bool operator==(const Copy<Cat>& copy, const Cat& e)
This doesn't match redCopy == Red() because Red() is of type Red, so the compiler deduces Red as the type of the second argument, i.e. Cat = Red, but then it expects the type of the first argument to be Copy<Red>, which it is not (redCopy's type is Copy<Apple>).
What you really want to express is something like
template <typename Cat>
bool operator==(const Copy<Cat>& copy, const something-that-derives-from-Cat& e)
The easiest way to do this, is to add a second template parameter:
template <typename Cat, typename DerivedFromCat>
bool operator==(const Copy<Cat>& copy, const DerivedFromCat& e)
Of course, this doesn't get the compiler to enforce that DerivedFromCat is actually derived from Cat. If you want this, you can use boost::enable_if:
template <typename Cat, typename DerivedFromCat>
typename enable_if<is_base_of<Cat, DerivedFromCat>, bool>::type
operator==(const Copy<Cat>&, const DerivedFromCat& e)
But that may be a bit of overkill...
But... How do you expect it to work? You declared a template operator
template<typename Cat>
bool operator==(const Copy<Cat>& copy, const Cat& e)
meaning that the type on the RHS is the same as template argument on the LHS (Cat in both cases). Yet you expect it to be called in case of
redCopy == Red()
where redCopy is Copy<Apple>. How?
Note: the template argument for redCopy is Apple, not Red. Your template operator simply can't possibly match these types.
If you had your redCopy declared as
Copy<Red> redCopy;
then your operator would work. Or if you did
redCopy == Apple()
your operator would work as well. But when you mix types like your original
Copy<Apple> redCopy;
redCopy == Red();
it simply can't work. What is your intent in this case?
#HighCommander4 explained what is wrong here. An alternative solution is to disable deduction for the second parameter of operator==. The type of the second parameter is then deduced solely based on the first argument of the ==-operator:
template<typename T> struct identity { typedef T type; };
template<typename Cat>
bool operator==(const Copy<Cat>& copy, typename identity<Cat>::type const& e) {
return *copy == e;
}
If you do it like this, there is no contradiction as to what type Cat is supposed to stand for, and the operator== will work as expected.

What are C++ non-template members as used in the Barton-Nackman trick?

From wikipedia:
// A class template to express an equality comparison interface.
template<typename T> class equal_comparable
{
friend bool operator==(T const &a, T const &b) { return a.equal_to(b); }
friend bool operator!=(T const &a, T const &b) { return !a.equal_to(b); }
};
class value_type
// Class value_type wants to have == and !=, so it derives from
// equal_comparable with itself as argument (which is the CRTP).
: private equal_comparable<value_type>
{
public:
bool equal_to(value_type const& rhs) const; // to be defined
};
This is supposed to be the Barton-Nackman, that could achieve compile-time dimensional analysis (checking if some operations applied to variables end up in comparable numbers, like speed comparable to space/time but no acceleration).
Could anyone explain me how, or at least explain me what are the NON-TEMPLATE members?
Thanks
The rules of the language have changed since the pattern was invented, although care was taken not to break it. In other words, as far as I can tell, it still works but for different reasons than it originally did. I don't think I would base an attempt at dimensional analysis on this pattern as I think there are better ways of doing that today.
I also think the example is too trivial to be helpful. As already stated the instantiation of equal_comparable<value_type> causes operator== and operator!= for value_type to appear. Since they are non-members it doesn't matter that the inheritance is private, they're still eligable for selection when resolving a call. It's just hard to see the point in this example. Let's say however, that you add a template parameter to equal_comparable and a few other things:
template<typename U, typename V> class equal_comparable
{
friend bool operator==(U const &a, V const &b) { return a.equal_to(b); }
friend bool operator!=(U const &a, V const &b) { return !a.equal_to(b); }
};
class some_other_type
{
bool equal_to(value_type const& rhs) const;
};
class value_type
: private equal_comparable<value_type>, // value_type comparable to itself
private equal_comparable<some_other_type> // value_type comparable to some_other_type
{
public:
bool equal_to(value_type const& rhs) const;
bool equal_to(some_other_type const& rhs) const;
};
Disclaimer: I have no idea if this is the way it's supposed to be used but I'm reasonably sure that it would work as described.
These are actually nontemplate nonmembers - the comparison operators in the base template - they get used by the ADL for the derived class. A template member would be something like:
class C
{
...
template < typename T > void DoGreatStuff( T t ) { ... }
...
};
The instantiation of equal_comparable<value_type> in value_type class causes the compiler to generate two comparison functions:
friend bool operator==(value_type const &a, value_type const &b) { return a.equal_to(b); }
friend bool operator!=(value_type const &a, value_type const &b) { return !a.equal_to(b); }
These functions are nontemplate since they do not depend on any template parameter, but they are also nonmembers since they are declared as friend.