I'm facing a curious compilation error in C++11.
I have a templated class that define an enum class:
template <typename Type>
class stats {
public:
// ...
enum class stat {
AVERAGE = (1 << 0),
STANDARD_DERIVATION = (1 << 1),
// ...
};
// ...
};
I currently want to use this enum in bitwise operations.
For example, here is an example of the usage of that enum:
template <typename Type>
void
stats<Type>::build(stat stats) {
if (stats & stat::AVERAGE)
this->build_average();
if (stats & stat::STANDARD_DEVIATION)
this->build_standard_deviation();
if (stats & stat::PERCENTILES)
this->build_percentiles();
if (stats & stat::LIMITS)
this->build_limits();
}
where we can call this function like this stats.build(stat::AVERAGE | stat::LIMITS).
In order to use the & or the | operators on that enum without having to manually cast to int each time, I have defined operators:
template<typename T>
using stat_t = typename eip::stats<T>::stat;
template <typename Type>
stat_t<Type>
operator|(const stat_t<Type>& lhs, const stat_t<Type>& rhs) {
return static_cast<stat_t<Type>>(static_cast<int>(lhs) | static_cast<int>(rhs));
}
template <typename Type>
stat_t<Type>
operator&(const stat_t<Type>& lhs, const stat_t<Type>& rhs) {
return static_cast<stat_t<Type>>(static_cast<int>(lhs) & static_cast<int>(rhs));
}
However, if I try to compile I get the following error:
error: invalid operands to binary expression ('eip::stats<double>::stat' and 'eip::stats<double>::stat')
if (stats & stat::PERCENTILES)
~~~~~ ^ ~~~~~~~~~~~~~~~~~
candidate template ignored: couldn't infer template argument 'Type'
operator&(const stat_t<Type>& lhs, const stat_t<Type>& rhs) {
^
I don't see why my overload is ignored. It seems that the compiler get the right types for both lhs and rhs (eip::stats<double>::stat) but it couldn't infer the template...
Moreover:
Explicitly calling the operator works (operator&<Type>(stats, stat::AVERAGE);)
I thought the problem was coming from the return type, but calling stat a = stats & stat::AVERAGE; doesn't work (same error as earlier).
Any idea?
There are two things wrong with your code.
This is a non-deducible context:
template <typename Type>
stat_t<Type>
operator&(const stat_t<Type>& lhs, const stat_t<Type>& rhs) { ... }
The idea is the same as if you had wrote:
template <typename T>
void foo(typename cls<T>::type ) { ... }
The compiler can't figure out what T could be there unless you tell it too. So you'd have to define your operator& in a place where we don't actually need to do deduction: make it a friend operator in the class itself:
friend stat operator&(const stat& lhs, const stat& rhs) { ... }
Once we fix that, we get to the other problem, which is that a stat isn't contextually convertable to bool so this expression:
if (stats & stat::AVERAGE)
won't compile. For that, you may want to have your operator& return an int or bool, or using this answer's idea, add an operator! and use it twice.
Related
I'm writing a class named Double which extends the built in type 'double' in c++. It has a data member of the type 'double'. For the class Double I need to overload many basic arithmetic operators like "+", "-", "*", "/". For example, the "+" operator is overloaded this way:
Relation<Double>* operator+ (Double &d2)
// Relation is a abstract class template.
{
/*
...code that do something else.
*/
return new Plus<Double>(this, &d2); // Plus is derived from Relation.
}
// Double + Double returns a Relation pointer.
and the "-" operator is overloaded fast the same way:
Relation<Double>* operator- (Double &d2)
{
/*
...code that do something else but the same as above.
*/
return new Minus<Double>(this, &d2);
}
The actual calculation is done by the member function in the Relation class. The only difference of the operators' bodies is the object initialized(Plus vs Minus).
For operator that takes exactly two operands, I should alway do the overloading like this, which is duplicated and not nice.
So template function comes to my mind. But the problem is, I can pass Plus or Minus as template argument, but can not pass the operator. How could I make a template or use other methods to do the overloading of these operators?
Yes, operator overloading may be a pain and source of code duplication, see this suggestion to the standard to ease it.
For now the only I can think about is something like this:
template<typename T>
struct OperatorMinus {
static T doIt(T const& lhs, T const& rhs) { return lhs - rhs; };
}
template<typename T>
struct OperatorPlus {
static T doIt(T const& lhs, T const& rhs) { return lhs + rhs; };
}
template<typename T, typename U>
class Operator: public Relation<T>
public:
Operator(T const& lhs, T const& rhs): _lhs(lhs), _rhs(rhs) {}
T doIt() override {
return U::doIt(_lhs, _rhs);
}
private:
T _lhs;
T _rhs;
};
Relation<Double>* operator+ (Double &d2)
{
return new Operator<Double, OperatorPlus<Double>>(this, &d2);
}
Overall, I'm implementing a class to modularize an unsigned number (a wrapper for unsigned types). I want my class to meet this conditions:
Do all the operations a modular number can do.
The underlined value_type must be of unsigned type, but the template parameter can be convertible to an unsigned type (for example, if the template parameter type is int, then the underlined value_type will be unsigned).
I want a derived class that can perfoms extra operations (like dividing, for example).
It must admit the operations to be compatible with types convertible to value_type (for example, Test<N> + unsigned, Test<N> + UnsignedWrapper, ...).
Must allow implicit conversion to the underlined value_type.
Must allow explicit conversions from Test<N> to Test<K> (same for TestChild), being N and K different numbers.
I reduced my problem to this code:
test.hpp:
#include <type_traits>
#include <iostream>
// As you can see, C++17 is needed
template <auto UInt>
class Test{
public:
//I need the type to be unsigned
typedef std::make_unsigned_t<decltype(UInt)> value_type;
static constexpr value_type N = static_cast<value_type>(UInt);
Test (const value_type& k = 0) : n(k%N){
// Just to show that this constructor is always called
std::cout << "Test user-defined constructor " << k << std::endl;
}
Test& operator+= (const Test& k){
n = (n+k.n)%N;
return *this;
}
template<typename U>
Test& operator+= (const U& other){
n = (n+static_cast<value_type>(other))%N;
return *this;
}
template<auto UInt2>
explicit operator Test<UInt2>() const{
return Test<UInt2>(n);
}
operator value_type() const{
// Just to show that this is called only once
std::cout << "Casting to value_type" << std::endl;
return n;
}
protected:
value_type n;
};
template<auto UInt>
class TestChild : public Test<UInt>{
public:
typedef typename Test<UInt>::value_type value_type;
static constexpr value_type N = Test<UInt>::N;
TestChild (const value_type& k = 0) : Test<UInt>(k){}
template<auto UInt2>
explicit operator TestChild<UInt2>() const{
return TestChild<UInt2>(this->n);
}
};
// I prefer to define binary operators outside the class and leave the logic inside the class
template<auto UInt>
const Test<UInt> operator+ (const Test<UInt>& lhs, const Test<UInt>& rhs){
return Test<UInt>(lhs) += rhs;
}
template<auto UInt>
const TestChild<UInt> operator+ (const TestChild<UInt>& lhs, const TestChild<UInt>& rhs){
return TestChild<UInt>(lhs) += rhs;
}
template<auto UInt, typename U>
const Test<UInt> operator+ (const Test<UInt>& lhs, const U& rhs){
return Test<UInt>(lhs) += static_cast<typename Test<UInt>::value_type>(rhs);
}
template<auto UInt, typename U>
const Test<UInt> operator+ (const U& lhs, const Test<UInt>& rhs){
return Test<UInt>(rhs) += static_cast<typename Test<UInt>::value_type>(lhs);
}
/****************************************************************************/
int main(){
// It doesn't matter how I initialize the varible,
// always calls the user-defined constructor
TestChild<89209> x(347), y(100), z(1000);
TestChild<89133> t = static_cast<decltype(t)>(x);
Test<10000> u = static_cast<decltype(u)>(y), v(z);
Test<19847> w(u);
TestChild<1297> r(u); //Here it seems that it casts u to its member value_type
u = u + v;
//u = u + w; //The compiler complains about ambiguity (don't know how to fix it without casting w)
//x = y + z; //No idea what's happening here
}
If I uncomment the last two sums, I get this error when compiling with g++ -std=c++17 -O2 -Wall -Wextra -pedantic test.cpp -o test, with GCC 7.2.0 in Ubuntu 16.04 LTS:
test.cpp: In function ‘int main()’:
test.cpp:92:10: error: ambiguous overload for ‘operator+’ (operand types are ‘Test<10000>’ and ‘Test<19847>’)
u = u + w; //The compiler complains about ambiguity (don't know how to fix it without casting w)
~~^~~
test.cpp:92:10: note: candidate: operator+(Test<10000>::value_type {aka unsigned int}, Test<19847>::value_type {aka unsigned int}) <built-in>
test.cpp:69:18: note: candidate: const Test<UInt> operator+(const Test<UInt>&, const U&) [with auto UInt = 10000; U = Test<19847>]
const Test<UInt> operator+ (const Test<UInt>& lhs, const U& rhs){
^~~~~~~~
test.cpp:74:18: note: candidate: const Test<UInt> operator+(const U&, const Test<UInt>&) [with auto UInt = 19847; U = Test<10000>]
const Test<UInt> operator+ (const U& lhs, const Test<UInt>& rhs){
^~~~~~~~
test.cpp: In instantiation of ‘const TestChild<UInt> operator+(const TestChild<UInt>&, const TestChild<UInt>&) [with auto UInt = 89209]’:
test.cpp:93:12: required from here
test.cpp:65:35: error: could not convert ‘(* & lhs).TestChild<89209>::<anonymous>.Test<89209>::operator+=<TestChild<89209> >((* & rhs))’ from ‘Test<89209>’ to ‘const TestChild<89209>’
return TestChild<UInt>(lhs) += rhs;
^~~
As you can see, it doesn't matter how I initialize the variables; the user-define constructor is always called. I initially thought that a casting to the underlined value_type was called, but then I realized that's not the case, because I explicited the casting to value_type and it still doesn't do any casting. I think the compiler must be doing something weird to avoid the copy constuctor or copy assignment, but I don't really know.
I understand the ambiguity in u = u + w;, which can be fixed by casting w; but I'd like to find a way to do it without casting (I mean, maybe deducing that u is of one type, so the sum must return that type).
The second sum is the one I don't really understand what the compiler error means. I've been looking for a solution for a week or so, and I can't figure out what to do. It seems that it gets the function signature right (the correct overload of operator+ is called), but then complains about some weird type conversion.
Maybe I'm laking in the design of the class. If that's the case, let me know.
EDIT: Note that u = w + u should also work, but it leads to another ambiguity, so I'm deciding to force the casting in order to do the operation.
This:
u = u + w;
is ambiguous because you have two operator+() overloads that are equall good matches:
template<auto UInt, typename U>
const Test<UInt> operator+(const Test<UInt>& lhs, const U& rhs);
template<auto UInt, typename U>
const Test<UInt> operator+(const U& lhs, const Test<UInt>& rhs);
Neither of those is more specialized than the other, so there's no way for the compiler to know which one to call (side-note, don't return const prvalues). The simplest solution here is to just add a 3rd overload which is more specialized than both:
template<auto UInt, auto UInt2>
Test<UInt> operator+(const Test<UInt>& lhs, const Test<UInt2>& rhs);
A less simple solution would be to constrain one or the other - on not being a Test:
template <typename T> struct is_test : std::false_type { };
template <auto V> struct is_test<Test<V>> : std::true_type { };
template<auto UInt, typename U,
std::enable_if_t<!is_test<U>{}, int> = 0>
Test<UInt> operator+(const U& lhs, const Test<UInt>& rhs);
But that seems overly complicated, probably unnecessary.
This:
x = y + z;
would invoke:
template<auto UInt>
const TestChild<UInt> operator+ (const TestChild<UInt>& lhs, const TestChild<UInt>& rhs);
which in turn calls operator+= on a copy of the left-hand side - which is actually Test<U>::operator+=, which returns a Test<U>.
However, the operator+ you're calling returns a TestChild<U>, which is a derived class - and Test<U> is not implicitly convertible to TestChild<U>. The easy fix here is to just perform the addition, but separate the return:
template<auto UInt>
TestChild<UInt> operator+ (TestChild<UInt> lhs, const TestChild<UInt>& rhs) {
lhs += rhs;
return lhs;
}
And now everything compiles.
I got stuck with a confusing problem here and i couldnt find any solution so far:
The linker complains about a multiple definition of an overloaded non-member operator==.
Imagine the following situation:
template <class T>
struct MyPtr
{
T* val;
...
}
template <class T> bool operator==(MyPtr<T> const & lhs, MyPtr<T> const & rhs)
{ return *lhs.val == *rhs.val; }
template <class T> bool operator==(MyPtr<T> const & lhs, T* const & rhs)
{ return *lhs.val == *rhs;}
So far so good, everything works like a charme, but as i tried to specialize my class to react to a char* in specific way things get weird:
template <>
struct MyPtr<char>
{
char* val;
...
}
//Now each of these functions result in a multiple definition error of the Linker,
//and i dont get why:
//bool operator== (MyPtr<char> const& lhs, MyPtr<char> const& rhs)
//{ return strcmp(lhs.val,rhs.val) == 0;}
//template <> bool operator==<char> (MyPtr<char> const& lhs, MyPtr<char> const& rhs)
//{ return strcmp(lhs.val,rhs.val) == 0;}
So what am i doing wrong here? The ordering in my code is as it is written here. Moving these function definitions above the Class specialization result in the error:
Error : specialization of 'MyPtr<char>' after instantiation
Error : redefinition of 'class MyPtr<char>'
Please notice i have to use GCC 4.1.2 . I hope its not a compiler problem here... again...
A function definition should only appear in a header file if:
It has been declared with the inline keyword, OR
It is defined within a class definition, OR
It involves at least one template parameter
(Those are among the cases where the multiple-identical-definitions version of ODR applies.)
So inline is not usually required with template functions. But an explicit specialization doesn't actually have any template parameters, so you should mark it inline if you want it in the header file.
Shouldn't this
template <> bool operator==<char> (MyPtr<char> const& lhs, MyPtr<char> const& rhs)
{ return strcmp(lhs.val,rhs.val) == 0;}
be
template <> bool operator==<char> (MyPtr<char> const& lhs, char* const& rhs)
{ return strcmp(lhs.val,rhs) == 0;}
?
Thanks to jogojapan for the corrections
The example below defines a basic podtype container class. Using this class a series of typedefs are then created which represent an OOP version of the basic podtype. The problem originates when we start assigning those types to one another.
I tried to define the operator as friend method with lhs and rhs arguments using plain PodObjects as type but without any succes. Is there anyone who might have experienced something simular or knows an other solution for this problem.
Thanks in advance.
#include <stdint.h>
template <typename T>
class PodObject {
protected:
T _value;
public:
PodObject<T>(int rhs) {
this->_value = static_cast<T>(rhs);
}
PodObject<T> operator+= (PodObject<T> const &rhs){
this->_value = rhs._value;
return *this;
}
};
typedef PodObject<int8_t> Int8;
typedef PodObject<int16_t> Int16;
int main() {
Int16 a = 10;
Int8 b = 15;
a += b; // Source of problem
return 0;
}
Results in a compiler output:
example.cpp:26:11: error: no viable overloaded '+='
a += b;
~ ^ ~
example.cpp:13:22: note: candidate function not viable: no known conversion from 'Int8' (aka 'PodObject<int8_t>') to 'const PodObject<short>'
for 1st argument
PodObject<T> operator+= (PodObject<T> const &rhs){
EDIT:
The friend method below does the job for me:
template<typename U, typename W>
friend PodObject<U> operator+= (PodObject<U> &lhs, PodObject<W> const &rhs) {
lhs._value += rhs._value;
return lhs;
}
You need a templated operator + because you are trying to add different types:
template <typename U>
PodObject<T> operator+= (PodObject<U> const &rhs){
this->_value = rhs._value;
return *this;
}
That said, the whole code looks like an anti-pattern. Your “OOP version of the basic podtype” is not a meaningful, nor generally useful, concept.
I have the following class
template<typename hi_t, typename lo_t>
struct int_t
{
hi_t hi;
lo_t lo;
int_t() : lo(0), hi(0) {}
int_t(int value) : lo(value), hi( value<0u? -1: 0 ) {}
int_t(unsigned value) : lo(value), hi( 0 ) {}
int_t& operator+=(const int_t& rhs)
{
lo_t _lo = lo;
lo += rhs.lo;
hi += rhs.hi;
hi += (int)(lo < _lo);
return *this;
}
template<typename hi_t, typename lo_t>
inline friend int_t<hi_t, lo_t> operator+(const int_t<hi_t, lo_t>&, const int_t<hi_t, lo_t>&);
};
template<typename hi_t, typename lo_t>
int_t<hi_t, lo_t> operator+(const int_t<hi_t, lo_t>& lhs, const int_t<hi_t, lo_t>& rhs)
{ return int_t<hi_t, lo_t>(lhs) += rhs; }
when executing the following code
typedef int_t<long long, unsigned long long> int128;
int main()
{
int128 i = 1024;
i = i + 20;
}
the compiler produce the error:
'int_t<hi_t,lo_t> operator +(const int_t<hi_t,lo_t> &,const int_t<hi_t,lo_t> &)' : could not deduce template argument for 'const int_t<hi_t,lo_t> &' from 'int'
when i put the code of template operator inside the class body - with removing the template line from the friend operator - it works, but with friend operator outside the class it can't deduce the operator. i thought when compiler generates code for this template operator the input parameters and return value will be of type int128 so it should have no problem from casting from int to that type.
UPDATE
if we define the friend operator inside the class as following the previous example works
template<typename hi_t, typename lo_t>
struct int_t
{
hi_t hi;
lo_t lo;
int_t() : lo(0), hi(0) {}
int_t(int value) : lo(value), hi( value<0u? -1: 0 ) {}
int_t(unsigned value) : lo(value), hi( 0 ) {}
int_t& operator+=(const int_t& rhs)
{
lo_t _lo = lo;
lo += rhs.lo;
hi += rhs.hi;
hi += (int)(lo < _lo);
return *this;
}
friend int_t operator+(const int_t& lhs, const int_t& rhs)
{ return int_t(lhs) += rhs; }
};
the problem happens when trying to define the template operator outside the class
The code is trickier than it seems at first look. The most tricky part is your declaration of a friend function. You should take a look at this answer regarding befriending a function from a template. The short recommendation is that you remove the templated operator+ and you implement it as a non-template friend function inside the class declaration:
template<typename hi_t, typename lo_t>
struct int_t
{
// ...
friend int_t operator+(int_t lhs, const int_t& rhs ) {
return lhs+=rhs;
}
};
As of the particular error, it might not be that helpful, and it might even be confusing, but you can start by taking into account that a template will only be taken into account for operator overloading if after type deduction it is a perfect match (i.e. no conversion required). That means that int128_t + int will never be matched by a templated operator+ that has the same type for both left and right hand side, even if there is a conversion.
The proposed solution above declares (and defines) a non-template function. Because it is defined inside the class, it will only be considered by Argument Dependent Lookup, and will thus only apply when one of the operators is a int_t, if it is found by ADL, then it will be picked up for overload resolution with the usual non-template rules, which means that the compiler is able to use any possible conversions to both the lhs or rhs (one of them must be an int_t instantiation if it was found by ADL, but it will convert the other).
Turn on all your compiler warnings.
You are using the same template parameter names in your friend declarations as in the template class itself, which is not good; rename them. Here's one solution: Remove the out-of-line operator definition and make the inline definition this:
template<typename H, typename L>
inline friend int_t operator+(const int_t & lhs, const int_t<H, L> & rhs)
{
return int_t(lhs) += rhs;
}
Now, since your RHS is an arbitrary type, you have to mention the type:
i = i + int128(20);
This is because there is no way to deduce parameters H,L from the integer 20 so that an appropriate conversion to int_t<H,L>(20) could be performed (see Nawaz's answer)!
To take advantage of your conversion constructor from int, you can only operate on the same type, not a templated other type. To this end, add a non-template operator:
int_t operator+(const int_t & rhs) const { return int_t(*this) += rhs; }
Now you can say i = i + 20;, using the int_t(int) constructor.
Update: As the OP suggests, to allow for symmetric invocation (i = 50 + i;), and in order to only allow operations within a fixed type as David suggests, we should remove both the unary operator and the templated friend binary operator and instead just have a non-templated binary friend:
friend int_t operator+(const int_t & lhs, const int_t & rhs) { return int_t(lhs) += rhs; }
That's a matter of design choice; I would personally favour the final version.
Are you certain that operator+= should be a member template? Usually, you just
inline friend int_t operator+(const int_t&, const int_t&) {...}
I'am not sure but maybe compiler needs template argument again
...
...
...
template<typename hi_t, typename lo_t>
int_t& operator+=(const int_t& rhs)
{
...
...
...