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
Related
Consider a struct
template<typename T, size_t N>
struct Something {
std::array<T,N> args;
// Some constructors
};
Now let's overload = operator for Something<T>. In fact I can implement it two ways.
First Way
Something& operator=(const Something& rhs){
// an implementation with copy semantics
}
Something& operator=(Something&& rhs) {
// an implementation with move semantics
}
Second Way
Something& operator=(Something rhs){
// implement with move semantics
}
So, my question is what is the most standard way and the most optimal way to do overloading First Way or Second Way?
For this particular case you should not implement the assignment operator. The compiler does that already for you:
#include <array>
#include <cstddef>
template<typename T, size_t N>
struct Something {
std::array<T,N> args;
};
int main() {
Something<int,42> a;
Something<int,42> b;
a = b;
}
Demo
For the general case I refer you to What are the basic rules and idioms for operator overloading?. And consider that not everything can be moved and not everything can be copied. Moreover, sometimes a move is just a copy. Hence, it depends.
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 have the following class definition:
template <typename T>
class MyBox {
public:
MyBox(T value) { _value = value; }
operator T() const { return _value; }
private:
T _value;
};
typedef MyBox<int> MyInt;
typedef MyBox<std::string> MyString;
When I try to use operators on my typedefs like this
bool first = MyInt(1) == MyInt(1); // works
bool second = std::string(MyString("a")) == std::string(MyString("a")); //works
bool third = MyString("a") == MyString("a"); // does not compile
the compiler complains about the third comparison
no operator "==" matches these operands. operand types are: MyString == MyString
and this happens with any other non-primitve boxing (e.g. MyBox<float> works but MyBox<std::map<int,int> > not. Why is that so?
This is especially unclear to me because for the first and second comparison the operator T() is used - why can't that be done automatically for MyString as well?
UPDATE: Is there a simple solution to this other than providing the specific operators for each non-primitive template? And what to do with MyString("a") == std::string("a")?
The reasons on why it works for built-in types, but does't work for custom types is answered in the following SO quesiton: using user-defined conversions with implicit conversions in comparisons. In short, this is because type conversion does not happen for template-deduced types. And while built-in operator== for int is not a template (and thus can be found using type conversion when MyBox<int> is used), operator== for std::string is a template.
However, the question mentioned above doesn't have details on how to solve this problem. Here is how: add following free functions
template<class T>
bool operator==(const MyBox<T>& lhs, const MyBox<T>& rhs) {
return static_cast<const T&>(lhs) == static_cast<const T&>(rhs);
}
template<class T>
bool operator==(const MyBox<T>& lhs, const T& rhs) {
return static_cast<const T&>(lhs) == rhs;
}
template<class T>
bool operator==(const T& lhs, const MyBox<T>& rhs) {
return lhs == static_cast<const T&>(rhs);
}
Herb Sutter's Guru of the Week #4, "Class Mechanics", teaches that the "a op b" form of an overloaded operator should be implemented in terms of the "a op= b" form (see point #4 in the solutions).
As an example, he shows how do to this for the + operator:
T& T::operator+=( const T& other ) {
//...
return *this;
}
T operator+( T a, const T& b ) {
a += b;
return a;
}
He points out that first parameter in operator+ is intentionally being passed by value, so that it can be moved if the caller passes a temporary.
Notice that this requires that the operator+ be a non-member function.
My question is, how can I apply this technique to an overloaded operator in a CRTP base class?
So say this is my CRTP base class with its operator+=:
template <typename Derived>
struct Base
{
//...
Derived operator+=(const Derived& other)
{
//...
return static_cast<Derived&>(*this);
}
};
I can see how to implement operator+ in terms of operator+= as a member function if I dispense with the "pass the first argument by value" optimization:
template <typename Derived>
struct Base
{
//...
Derived operator+(const Derived& other) const
{
Derived result(static_cast<const Derived&>(*this);
result += other;
return result;
}
};
but is there a way to do this while using that optimization (and therefore making the operator+ a nonmember)?
The normal way to implement Herb's advice is as follows:
struct A {
A& operator+=(cosnt A& rhs)
{
...
return *this;
}
friend A operator+(A lhs, cosnt A& rhs)
{
return lhs += rhs;
}
};
Extending this to CRTP:
template <typename Derived>
struct Base
{
Derived& operator+=(const Derived& other)
{
//....
return *self();
}
friend Derived operator+(Derived left, const Derived& other)
{
return left += other;
}
private:
Derived* self() {return static_cast<Derived*>(this);}
};
If you try to avoid the use of friend here, you realize it's almost this:
template<class T>
T operator+(T left, const T& right)
{return left += right;}
But is only valid for things derived from Base<T>, which is tricky and ugly to do.
template<class T, class valid=typename std::enable_if<std::is_base_of<Base<T>,T>::value,T>::type>
T operator+(T left, const T& right)
{return left+=right;}
Additionally, if it's a friend internal to the class, then it's not technically in the global namespace. So if someone writes an invalid a+b where neither is a Base, then your overload won't contribute to the 1000 line error message. The free type-trait version does.
As for why that signature: Values for mutable, const& for immutable. && is really only for move constructors and a few other special cases.
T operator+(T&&, T) //left side can't bind to lvalues, unnecessary copy of right hand side ALWAYS
T operator+(T&&, T&&) //neither left nor right can bind to lvalues
T operator+(T&&, const T&) //left side can't bind to lvalues
T operator+(const T&, T) //unnecessary copies of left sometimes and right ALWAYS
T operator+(const T&, T&&) //unnecessary copy of left sometimes and right cant bind to rvalues
T operator+(const T&, const T&) //unnecessary copy of left sometimes
T operator+(T, T) //unnecessary copy of right hand side ALWAYS
T operator+(T, T&&) //right side cant bind to lvalues
T operator+(T, const T&) //good
//when implemented as a member, it acts as if the lhs is of type `T`.
If moves are much faster than copies, and you're dealing with a commutative operator, you may be justified in overloading these four. However, it only applies to commutative operators (where A?B==B?A, so + and *, but not -, /, or %). For non-commutative operators, there's no reason to not use the single overload above.
T operator+(T&& lhs , const T& rhs) {return lhs+=rhs;}
T operator+(T&& lhs , T&& rhs) {return lhs+=rhs;} //no purpose except resolving ambiguity
T operator+(const T& lhs , const T& rhs) {return T(lhs)+=rhs;} //no purpose except resolving ambiguity
T operator+(const T& lhs, T&& rhs) {return rhs+=lhs;} //THIS ONE GIVES THE PERFORMANCE BOOST
I cannot figure out if the std::move in the following code does anything good or that it is completely wrong?
The class Object has both Move and Copy constructor defined.
First: With Move:
template<typename T> template <typename F>
const Object<T> Object<T>::operator*(const F& rhs) const
{
return std::move(Object(*this) *= rhs); // We end in move constructor
}
Second: Without Move:
template<typename T> template <typename F>
const Object<T> Object<T>::operator*(const F& rhs) const
{
return Object(*this) *= rhs; // We end in copy constructor
}
The *= operator is defined as:
template<typename T> template<typename F>
Object<T>& Object<T>::operator*=(const F& rhs)
{
for(int i = 0; i < dimension ; i++)
{
_inner[i] *= rhs;
}
return *this;
}
Here is the code i use to test it:
Object<double> test(4);
Object<double> test2(test * 4);
std::cout << test2; // works fine
Result
In the first case we end in the move constructor and in the second we end in the copy constructor.
In either case the code compiles.
Is one more efficient than the other since I would assume it is faster to move the new object out instead of copying it out?
Additional info:
I use the following compiler: g++ (Ubuntu/Linaro 4.7.3-1ubuntu1) 4.7.3
Is one more efficient than the other since I would assume it is faster to move the new object out instead of copying it out?
Yes, using std::move here will be more efficient, assuming the object has move semantics more efficient than copying.
Usually, when returning a temporary or a local variable, move semantics will be used automatically. However, in this case you're not directly returning the temporary, but rather the lvalue reference returned by operator*=. Since an lvalue won't be moved, you do need std::move in this case to turn it into an rvalue.
However, you should not return a const value, as this prevents the return value from being used to move-initialise (or move-assign to) another object. Your example will initialise test2 by copying the return value, although that copy might be elided.
Alternatively, you could implement it using a local variable:
template<typename T> template <typename F>
Object<T> Object<T>::operator*(const F& rhs) const
{
Object lhs(*this);
lhs *= rhs;
return lhs;
}
Not only can the return value be moved, but the move itself can be elided.