I am porting old C++ code to an Effective C++ safe version. I read about problems with this flag, but as I don't have a choice wether to do it or not, it has to be. I came along a problem with overloading the equals operator when using a template.
I tried to move the functionality from header files to the actual cpp's, but this didn't have any effect. Removing the template worked, but as the template is needed, it just showed me where the problem is.
template <typename T1, typename T2>
MyClass& operator = ( const boost::tuple<T1,T2> & _v )
{
//Some operations
return *this;
}
Compiling this code throws the following error:
'operator=' should return a reference to *this
pointing at the exact line return *this;
MyClass does not inherit any other class. Is this a effc++ bug or is there anything I am missing?
Related
I'm learning how to use std::chrono and I want to make a template class Timer easy to use (defined in timer.h). The testing program was successful and everything worked fine, until I tried to use my new Timer in a program with the definition of some template operators, which conflit with the operators used inside Timer.
Inside Timer I have to use operator- between two variables (start_time and end_time) of type std::chrono::time_point, in order to obtain the duration variable containing the elapsed time.
In another header (algebra.h) I implemented the overloading of the binary operator- to make the difference between two std::vector or two std::array, or also a user-defined container provided with operator[] and size() member function.
template<typename pointType>
pointType operator-(pointType a, const pointType & b){
for(int i = 0; i < a.size(); ++i){
a[i] = a[i] - b[i];
}
return a;
}
When I try to include both timer.h and algebra.h, the compiler throws an error saying "ambiguous overload for operator-" suggesting, as possible candidates, both the operator in algebra.h and the one implemented in <chrono>.
I don't understand why it is ambiguous, since pointType can't be deduced as std::chrono::time_point because it doesn't have operator[] and size() member function.
P.S. I tried something else to work it out, but I only got more confused testing a program which use std::valarray. When I include both <valarray> and "algebra.h", and try to make a difference between two valarrays, I expected the compiler to complain about ambiguous definition of operator-, since std::valarray already has implementation for binary operators. But this doesn't happen: it compiles using the <valarray> implementation. Why this doesn't throw an error?
It is ambiguous because the compiler only looks at the function signature to test for ambiguity, not the body of the function. In your example, this is the function signature:
template<typename pointType>
pointType operator-(pointType a, const pointType & b)
Here, the template parameter pointType could be deduced as std::chrono::time_point. However, there is already a binary minus operator declared in the chrono header for std::chrono::time_point (https://en.cppreference.com/w/cpp/chrono/time_point/operator_arith2). This is what is causing the ambiguity error.
To solve this problem, you should first consider whether you need such a generic binary minus operator. The problem you are currently experiencing will not be unique to std::chrono::time_point, but will also occur with any other header that contains a class with a member or non-member binary minus operator, where both arguments are of the same type (or could implicitly convert into the same type). Perhaps a simple set of function overloads for the types in question:
template<typename T>
std::vector<T> operator-(const std::vector<T>& a, const std::vector<T>& b);
template<typename T, size_t N>
std::array<T,N> operator-(const std::array<T,N>& a, const std::array<T,N>& b);
This would be the safest option. You could also not use operator overloading altogether, and stick to a conventional function:
template<typename T>
T pointwise_subtract(const T& a, const T& b);
If you have a c++20 compiler, you could use concepts. If you insist on using non-member operator templates, you may have to use SFINAE-based template metaprogramming, a more advanced and less readable technique:
//enable this template if the type T has a member method "size" and
// subscript operator accepting variables of type "size_t"
template<typename T, typename=std::void_t<
decltype(std::declval<T>().size()),
decltype(std::declval<T>()[std::declval<size_t>()])
>
T operator-(const T& a, const T& b);
This will remove your ambiguity error.
I've written a templated class that implements some basic operator overloading, following the guidelines provided by this particularly insightful answer:
template <typename _type>
class myClass {
// ...
template<_input_type> myClass<_type>& operator+=(const myClass<_input_type>& _other);
// ...
}
with the arithmetic compound operators written as members:
template <typename _type>
template <typename _input_type>
myClass<_type>& myClass<_type>::operator+=(const myClass<_input_type>& _other) {
static_assert(std::is_arithmetic<_type>::value);
// do stuff
return *this;
};
and the non-compound operator as a non-member:
template <typename _type, typename _input_type>
inline myClass<_type> operator+(myClass<_type>& _o1, myClass<_input_type> _o2) {
return _o1+=_o2;
};
However, due to the template myClass can be used for several data types, some of them non-numeric that can't handle +,-,* or / operators, and as such I was wondering what are the downsides of implementing all operator overloading code as non-member functions, so that e.g. I could just placed them all on a separate header file that would only need to be included if there is need for arithmetic functionality. I understand one solution would be to define a new class myNumericClass : public myClass that just implements operator overloading, but that would require a new typename and limit the versatility of myClass.
The primary shortcoming of implementing compound assignment as a non-member is inconsistency with the simple (copy or move) assignment operator. A simple copy or move assignment (i.e., operator=) must be implemented as a member function, or the compiler will outright reject the code.
Given that copy/move assignment must be implemented as member functions, many prefer to implement compound assignment as members as well.
As an aside, this code:
template <typename _type, typename _input_type>
inline myClass<_type> operator+(myClass<_type>& _o1, myClass<_input_type> _o2) {
return _o1+=_o2;
};
...is, IMO, highly inadvisable. The general style is fine, but you've mixed up which operand to pass by value and which to pass by reference. As a result, it may be needlessly inefficient, and (much worse) modifies its left operand, so it really acts like += instead of +. What you almost certainly want is more like this:
template <typename _type, typename _input_type>
inline myClass<_type> operator+(myClass<_type> _o1, myClass<_input_type> const &_o2)
{
return _o1+=_o2;
};
Here we pass the left operand by value, so when the function is called a temporary value is created and initialized from the left operand. We then modify that temporary value (without changing the original) and return it. Since we do return it, there will be copy elision (optional on older compilers, but mandatory since C++17) which means it'll normally really just be a reference to the destination, so effectively, something like: a = b + c; will be treated as: a = b; a += c;. Since we only need the previous value of the right operand, we pass it as a reference to const to avoid an unnecessary copy (though, depending on the type, passing by reference may not gain enough to care about, or could even be a loss. But it can be a big gain, and is rarely more than a tiny loss).
This question already has answers here:
Overloading friend operator << for template class [duplicate]
(3 answers)
Operator << overloading
(1 answer)
Closed 4 years ago.
My question is different because it is pertaining to a newer version of C++.
This question was asked already here and I'm referring to this answer. However this was asked and answered back in 2009 and it is quite dated to the current compilers and versions of the C++ language. However; here it is again.
I have a simple template class that I would like to overload the stream operator, but in my case here: I'm using Visual Studio 2017 CE v4.6.01055 with compiler c++ language feature set to ISO C++ Latest Draft Standard (/std:c++latest) simply put C++17. I'm build it in x86 debug mode.
I have tried the solution from the Q/A above but it keeps giving me compiler errors.
Here is the simple class
template<class T>
class Point {
public:
T mX;
T mY;
Point() : mX(0), mY(0) {}
Point( T x, T y ) : mX( x ), mY( y ) {}
Point( T& x, T& y ) : mX( x ), mY( y ) {}
Point( T* x, T* y ) : mX( *x ), mY( *y ) {}
friend operator<<( std::ostream& out, const Point<T>& p );
}
The suggestion stated that you should make a single instance called specialization of that template a friend. The user also stated that you needed to put the declaration of operator<< above the class template declaration. I've tried that; I've tried moving the method out the header into the cpp, below, above it; I tried to even put the definition within the class as well, and no matter what I try; I can not get it to compile correctly.
I even found this answer and I tried the pattern there but it is still failing in c++17. At least at this point it is compiling without the <> specialization and failing to build, but when I add that in it fails to compile.
What is the proper syntax and or placement to overload the stream operator as a friend to a simple class template in c++17? I don't necessarily mean the syntax of the actual operator<<() itself... it had to deal more with the actual placement of the declarations and definitions. The only piece of syntax that I wasn't sure of but found out from the previous answers that I've shown here was the part about making it a specialization; that part was new to me.
EDIT
I don't know what was causing the compiler or linker errors. The declarations and definitions were the same. I ended up changing the 2nd param from const Point<T>& to Point<T> in the declarations and definition and it compiled and built. I then went ahead and changed them back to const Point<T>& and it now compiles, builds and gives me the output without error.
It could of been a bug within Visual Studio since I did just update it to the newest version last night. I don't know what was causing it; but what ever it was is now resolved.
The syntax for a friend function is almost the same as with any other function. In your case, you would do:
friend std::ostream& operator<< (std::ostream& out, const Point<T>& p )
{
//do whatever
return out;
}
This makes an operator that takes in a Point<T> and prints it however you want to. For more info, look here.
EDIT:
In terms of usage and placement, you can only use the function when the class is defined, so it is general practice to declare it in the same files as your class. The only real difference between a class friend function and a template friend function is that the compiler has to interpret the friend function for each data type the friend object is. This is part of why having it defined in the same files as the template is the norm. Because of this, it is, for all intents and purposes, also a template function,hence the specialization. If you declare it outside the body of the template, you need to treat it as such.
I tried searching for possible implementations of std::ignore, but failed to find a clear answer.
http://mail-archives.apache.org/mod_mbox/stdcxx-dev/200807.mbox/%3C4872AA41.7040005#roguewave.com%3E cites problems with c++ spec and provides and illustrative implementation as
namespace std {
struct _Ignore
{
template <class _Type>
_Ignore& operator= (const _Type& value) { return *this; }
};
const _Ignore ignore = _Ignore ();
} // namespace std
And there are further discussions about the problems. So, how the current implementations looks like? and why?
In GCC it looks like what you posted except that ignore is not const. That overcomes what looks to be the primary objection in the thread you linked.
In Clang the assignment operator is const, and also returns const, and the entire class is itself a template (in addition to the assignment operator being a template). The template instantiation uses unsigned char for whatever reason.
Kind of a random question...
What I'm looking for is a way to express a cast operation which uses a defined operator of the class instance I'm casting from, and generates a compile-time error if there is not a defined cast operator for the type. So, for example, what I'm looking for is something like:
template< typename RESULT_TYPE, typename INPUT_TYPE >
RESULT_TYPE operator_cast( const INPUT_TYPE& tValue )
{
return tValue.operator RESULT_TYPE();
}
// Should work...
CString sString;
LPCTSTR pcszString = operator_cast< LPCTSTR >( sString );
// Should fail...
int iValue = 42;
DWORD dwValue = operator_cast< DWORD >( iValue );
Interesting side-note: The above code crashes the VS2005 C++ compiler, and doesn't compile correctly in the VS2008 C++ compiler due to what I'm guessing is a compiler bug, but hopefully demonstrates the idea.
Anybody know of any way to achieve this effect?
Edit: More rationale, to explain why you might use this. Say you have a wrapper class which is supposed to encapsulate or abstract a type, and you're casting it to the encapsulated type. You could use static_cast<>, but that might work when you wanted it to fail (ie: the compiler chooses an operator which is allowed to convert to the type you asked for, when you wanted a failure because that operator is not present).
Admittedly it's an uncommon case, but it's annoying that I can't express exactly what I want the compiler to do in an encapsulated function... hence the question here.
The code you posted works with the Cameau compiler (which is usually a good indication that it's valid C++).
As you know a valid cast consists of no more than one user defined cast, so a possible solution I was thinking of was adding another user defined cast by defining a new type in the cast template and having a static assert that no cast is available from the new type to the result type (using boost is_convertible), however this doesn't distinguish between cast operators and cast constructors (ctor with one argument) and alows additional casts to take place (e.g. void* to bool). I'm not sure if making a distinction between cast operators and cast constructors is the the correct thing to do but that's what the question states.
After a couple of days mulling this over it hit me, you can simply take the address of the cast operator. This is slightly easier said than done due to C++'s hairy pointer to member syntax (it took me way longer than expected to get it right). I don't know if this works on VS2008, I only checked it on Cameau.
template< typename Res, typename T>
Res operator_cast( const T& t )
{
typedef Res (T::*cast_op_t)() const;
cast_op_t cast_op = &T::operator Res;
return (t.*cast_op)();
}
Edit: I got a chance to test it on VS2005 and VS2008. My findings differ from the original poster's.
On VS2008 the original version seems to work fine (as does mine).
On VS2005 the original version only crashes the compiler when casting from a built in type (e.g. casting int to int) after providing a compilation error which doesn't seem so bad too me and my version seems to works in all cases.
Using a converting constructor marked explicit is how you would prevent the compiler from allowing implicitly converted types from initializing your wrapper class.
As template-related compiler error messages are usually a complete pain to unravel, if you don't mind specifying each conversion you can get the compiler to emit a more instructive message in the fail case by providing a default template definition too. This uses the fact that the compiler will only attempt to compile code in templates that is actually invoked.
#include <string>
// Class to trigger compiler warning
class NO_OPERATOR_CONVERSION_AVAILABLE
{
private:
NO_OPERATOR_CONVERSION_AVAILABLE(){};
};
// Default template definition to cause compiler error
template<typename T1, typename T2> T1 operator_cast(const T2&)
{
NO_OPERATOR_CONVERSION_AVAILABLE a;
return T1();
}
// Template specialisation
template<> std::string operator_cast(const std::string &x)
{
return x;
}
sounds like you want template specialization, something like this would do:
/* general template */
template<typename T1, typename T2> T1 operator_cast(const T2 &x);
/* do this for each valid cast */
template<> LPCTSTR operator_cast(const CString &x) { return (LPCTSTR)x; }
EDIT: As noted in another post, you can put something in the general version to give you a more useful error message if an unsupported cast is performed.