Consider the following classes, where the first is templated. Both are meant to hold a numeric value.
template<typename T>
struct foo
{
foo(T val) : val{ val } {}
T val;
};
struct bar
{
bar(double val) : val{ val } {}
double val;
};
I want to define a way to add these classes together to get a new one with a different value.
template<typename T>
foo<T> operator+(foo<T> a, foo<T> b)
{
return foo<T>(a.val + b.val);
}
bar operator+(bar a, bar b)
{
return bar(a.val + b.val);
}
When I use these operators with implicit conversion, the operator using object of type foo doesn't use the implicit conversion on the double value to apply my overloaded operator, even though it can do it for the non-template class. The result is that there is no operator matching the types in that expression.
int main()
{
foo<double> foo_value(11.0);
bar bar_value(11.0);
foo<double> ev1 = foo_value + 1.0; // no operator matches these operands
bar ev2 = bar_value + 1.0;
}
Does the operator have to be explicitly instantiated first? If so, a) how does that look like, and b) why isn't the instantiation done implicitly, if it can be done when initializing an object of type foo<double>?
If the standard doesn't support any sort of resolution without explicitly casting 1.0 to a value of type foo<double>, I presume the only other possibility is defining operator overloads for each type I want to use like that (for both lhs and rhs)?
The thing you have to remember about templates is that they will not do a conversion for you. All they do is try and figure out the types things are, and if that jives with template parameters, then it will stamp out the function and call it.
In you case when you do
foo_value + 1.0
the compiler goes okay, lets see if we have any operator + that will work for this. It finds
template<typename T>
foo<T> operator+(foo<T> a, foo<T> b)
{
return foo<T>(a.val + b.val);
}
and then it tries to figure out what T is so it can stamp out a concrete function. It looks at foo_value, sees it is a foo<double> so it says for the first parameter T needs to be a double. Then it looks at 1.0 and goes okay, I have a double and that is when you run into a problem. The compiler can't deduce what T should be for b because it expects a foo<some_type>, but got a double instead. Because it can't deduce a type, your code fails to compile.
In order to get the behavior you want you would need to add
template<typename T>
foo<T> operator+(foo<T> a, T b)
{
return foo<T>(a.val + b);
}
Which lets you add a T to a foo<T>, or better yet
template<typename T, typename U>
foo<T> operator+(foo<T> a, U b)
{
return foo<T>(a.val + b);
}
Which lets you add anything to a foo<T> (foo<double> + int for instance where the first version would not allow that)
Related
Consider I have a custom type (which I can extend):
struct Foo {
int a;
string b;
};
How can I make an instance of this object assignable to a std::tie, i.e. std::tuple of references?
Foo foo = ...;
int a;
string b;
std::tie(a, b) = foo;
Failed attempts:
Overloading the assignment operator for tuple<int&,string&> = Foo is not possible, since assignment operator is one of the binary operators which have to be members of the left hand side object.
So I tried to solve this by implementing a suitable tuple-conversion operator. The following versions fail:
operator tuple<int,string>() const
operator tuple<const int&,const string&>() const
They result in an error at the assignment, telling that "operator = is not overloaded for tuple<int&,string&> = Foo". I guess this is because "conversion to any type X + deducing template parameter X for operator=" don't work together, only one of them at once.
Imperfect attempt:
Hence I tried to implement a conversion operator for the exact type of the tie:
operator tuple<int&,string&>() const Demo
operator tuple<int&,string&>() Demo
The assignment now works since types are now (after conversion) exactly the same, but this won't work for three scenarios which I'd like to support:
If the tie has variables of different but convertible types bound (i.e. change int a; to long long a; on the client side), it fails since the types have to fully match. This contradicts the usual use of assigning a tuple to a tuple of references which allows convertible types.(1)
The conversion operator needs to return a tie which has to be given lvalue references. This won't work for temporary values or const members.(2)
If the conversion operator is not const, the assignment also fails for a const Foo on the right hand side. To implement a const version of the conversion, we need to hack away const-ness of the members of the const subject. This is ugly and might be abused, resulting in undefined behavior.
I only see an alternative in providing my own tie function + class together with my "tie-able" objects, which makes me force to duplicate the functionality of std::tie which I don't like (not that I find it difficult to do so, but it feels wrong to have to do it).
I think at the end of the day, the conclusion is that this is one drawback of a library-only tuple implementation. They're not as magic as we'd like them to be.
EDIT:
As it turns out, there doesn't seem to be a real solution addressing all of the above problems. A very good answer would explain why this isn't solvable. In particular, I'd like someone to shed some light on why the "failed attempts" can't possibly work.
(1): A horrible hack is to write the conversion as a template and convert to the requested member types in the conversion operator. It's a horrible hack because I don't know where to store these converted members. In this demo I use static variables, but this is not thread-reentrant.
(2): Same hack as in (1) can be applied.
Why the current attempts fail
std::tie(a, b) produces a std::tuple<int&, string&>.
This type is not related to std::tuple<int, string> etc.
std::tuple<T...>s have several assignment-operators:
A default assignment-operator, that takes a std::tuple<T...>
A tuple-converting assignment-operator template with a type parameter pack U..., that takes a std::tuple<U...>
A pair-converting assignment-operator template with two type parameters U1, U2, that takes a std::pair<U1, U2>
For those three versions exist copy- and move-variants; add either a const& or a && to the types they take.
The assignment-operator templates have to deduce their template arguments from the function argument type (i.e. of the type of the RHS of the assignment-expression).
Without a conversion operator in Foo, none of those assignment-operators are viable for std::tie(a,b) = foo.
If you add a conversion operator to Foo,
then only the default assignment-operator becomes viable:
Template type deduction does not take user-defined conversions into account.
That is, you cannot deduce template arguments for the assignment-operator templates from the type Foo.
Since only one user-defined conversion is allowed in an implicit conversion sequence, the type the conversion operator converts to must match the type of the default assignment operator exactly. That is, it must use the exact same tuple element types as the result of std::tie.
To support conversions of the element types (e.g. assignment of Foo::a to a long), the conversion operator of Foo has to be a template:
struct Foo {
int a;
string b;
template<typename T, typename U>
operator std::tuple<T, U>();
};
However, the element types of std::tie are references.
Since you should not return a reference to a temporary,
the options for conversions inside the operator template are quite limited
(heap, type punning, static, thread local, etc).
There are only two ways you can try to go:
Use the templated assignment-operators:
You need to publicly derive from a type the templated assignment-operator matches exactly.
Use the non-templated assignment-operators:
Offer a non-explicit conversion to the type the non-templated copy-operator expects, so it will be used.
There is no third option.
In both cases, your type must contain the elements you want to assign, no way around it.
#include <iostream>
#include <tuple>
using namespace std;
struct X : tuple<int,int> {
};
struct Y {
int i;
operator tuple<int&,int&>() {return tuple<int&,int&>{i,i};}
};
int main()
{
int a, b;
tie(a, b) = make_tuple(9,9);
tie(a, b) = X{};
tie(a, b) = Y{};
cout << a << ' ' << b << '\n';
}
On coliru: http://coliru.stacked-crooked.com/a/315d4a43c62eec8d
As the other answers already explain, you have to either inherit from a tuple (in order to match the assignment operator template) or convert to the exact same tuple of references (in order to match the non-templated assignment operator taking a tuple of references of the same types).
If you'd inherit from a tuple, you'd lose the named members, i.e. foo.a is no longer possible.
In this answer, I present another option: If you're willing to pay some space overhead (constant per member), you can have both named members and tuple inheritance simultaneously by inheriting from a tuple of const references, i.e. a const tie of the object itself:
struct Foo : tuple<const int&, const string&> {
int a;
string b;
Foo(int a, string b) :
tuple{std::tie(this->a, this->b)},
a{a}, b{b}
{}
};
This "attached tie" makes it possible to assign a (non-const!) Foo to a tie of convertible component types. Since the "attached tie" is a tuple of references, it automatically assigns the current values of the members, even though you initialized it in the constructor.
Why is the "attached tie" const? Because otherwise, a const Foo could be modified via its attached tie.
Example usage with non-exact component types of the tie (note the long long vs int):
int main()
{
Foo foo(0, "bar");
foo.a = 42;
long long a;
string b;
tie(a, b) = foo;
cout << a << ' ' << b << '\n';
}
will print
42 bar
Live demo
So this solves problems 1. + 3. by introducing some space overhead.
This kind of does what you want right? (assumes that your values can be linked to the types of course...)
#include <tuple>
#include <string>
#include <iostream>
#include <functional>
using namespace std;
struct Foo {
int a;
string b;
template <template<typename ...Args> class tuple, typename ...Args>
operator tuple<Args...>() const {
return forward_as_tuple(get<Args>()...);
}
template <template<typename ...Args> class tuple, typename ...Args>
operator tuple<Args...>() {
return forward_as_tuple(get<Args>()...);
}
private:
// This is hacky, may be there is a way to avoid it...
template <typename T>
T get()
{ static typename remove_reference<T>::type i; return i; }
template <typename T>
T get() const
{ static typename remove_reference<T>::type i; return i; }
};
template <>
int&
Foo::get()
{ return a; }
template <>
string&
Foo::get()
{ return b; }
template <>
int&
Foo::get() const
{ return *const_cast<int*>(&a); }
template <>
string&
Foo::get() const
{ return *const_cast<string*>(&b); }
int main() {
Foo foo { 42, "bar" };
const Foo foo2 { 43, "gah" };
int a;
string b;
tie(a, b) = foo;
cout << a << ", " << b << endl;
tie(a, b) = foo2;
cout << a << ", " << b << endl;
}
Major downside is that each member can only be accessed by their types, now, you could potentially get around this with some other mechanism (for example, define a type per member, and wrap the reference to the type by the member type you want to access..)
Secondly the conversion operator is not explicit, it will convert to any tuple type requested (may be you don't want that..)
Major advantage is that you don't have to explicitly specify the conversion type, it's all deduced...
This code works for me. I'd love it if someone could point out anything wrong with it.
Simple Version on Compiler Explorer
More Generic Version on Compiler Explorer
#include <tuple>
#include <cassert>
struct LevelBounds final
{
int min;
int max;
operator std::tuple<int&, int&>() { return {min, max}; }
};
int main() {
int a, b;
auto lb = LevelBounds{30, 40};
std::tie(a, b) = lb;
assert(30 == a);
assert(40 == b);
return 0;
}
I am trying to built a templated num class. This class needs to have a public attribute, val, with type T, which is the only templated parameter. Furthermore if one provides a value the attribute (val) should be initialized with this value. To do so I made the following code:
#include <iostream>
template<class T>
class Num {
public:
T val;
Num():val(0) { std::cout<<"default constr used"<<std::endl; }
Num(T value):val(value) {std::cout<<"constr (T value) used"<<std::endl; }
~Num() { std::cout<<"destructor used"<<std::endl; }
template<typename U>
Num operator+(const Num<U>& other) {
return val+other.value;
}
};
Furthermore I created the main() function to test the program, which looks like this:
int main() {
std::cout << Num<int>(1) + Num<double>(2.0);
return 0;
}
However the result of the program is now 3. Whereas I expected it to be 3.0 (of type double).
For that you will need to change the return type.
In your code:
// vvv---- Means Num<T>
Num operator+(const Num<U>& other) {
return val + other.val;
}
Indeed, inside a class template, you can type the name of the class without template arguments and it's gonna be somewhat equivalent to writing Num<T>.
Your function is always returning the type of the first operant, no matter the type of the addition itself.
What you want is to deduce that type coming from the addition:
auto operator+(const Num<U>& other) -> Num<decltype(val + other.val)> {
return val + other.val;
}
That way, it's always the right return type according to the C++ operator rules.
operator+ should be symmetric with respect to its arguments. It's better be implemented as a free function rather than a member function to make this symmetry explicit.
For example (using C++14 return type deduction):
template<class T, class U>
auto operator+(const Num<T>& x, const Num<U>& y) {
using R = decltype(std::declval<T>() + std::declval<U>());
return Num<R>{x.val + y.val};
}
std::declval<T>() is there for genericity, if T and/or U are not default constructible. If types are limited to built-in ones, like int and double, it can be replaced with T{} or T():
using R = decltype(T{} + U{});
With class template argument deduction in C++17 it can be simplified further:
template<class T, class U>
auto operator+(const Num<T>& x, const Num<U>& y) {
return Num{x.val + y.val};
}
What is the reason of the following rule, "a user-defined conversion function template cannot have a deduced return type."
struct S {
operator auto() const { return 10; } // OK
template<class T> operator auto() const { return 42; } // error
};
Even if it was allowed, in the second line, there is nothing that depends on the template.
It can't be called (what is the purpose of T in that case ?)
If you want to convert to a user defined type, then you'll do that:
Let's say you have:
struct S
{
template<typename T> operator T() { return T(42); }
};
That's clear and there is no need to deduce anything.
You'd call this like this:
S s;
int v = s;
float f = s;
Please notice that, in that case, using auto instead of float in the code above would prevent the compiler to deduce the type (is it a float ? an int ? an Orange ?). The sentence above simply explains that.
This is the basic implementation I'm using for expression templates, based on the CRTP which allows me to conveniently combine multiple types of operations without also asking everything to be an expression tree.
template<typename E>
struct OpExpression
{
auto eval(iter_type n) const
{
return static_cast<E const&>(*this).eval(n);
}
};
template<typename T>
struct OpFrame : OpExpression<OpFrame<T>>
{
T *ptr;
OpFrame(T *ptr) : ptr{ ptr } {}
T eval(iter_type n) const
{
return ptr[n];
}
};
template<typename T>
struct OpLiteral : OpExpression<OpLiteral<T>>
{
T value;
OpLiteral<T>(T value) : value{ value } {}
T eval(iter_type) const
{
return value;
}
operator T() const
{
return value;
}
};
Here is a class to apply the addition between two values (and an operator overload to make it pretty):
template<typename E1, typename E2>
struct OpFrameAdd : OpExpression<OpFrameAdd<E1, E2>>
{
OpFrameAdd(E1 const a, E2 const b) : a{ a }, b{ b } {}
auto eval(iter_type n) const
{
return a.eval(n) + b.eval(n);
}
protected:
E1 const a;
E2 const b;
};
template<typename E1, typename E2>
auto operator+(OpExpression<E1> const& a, OpExpression<E2> const& b)
{
auto v = OpFrameAdd<E1, E2>(*static_cast<const E1*>(&a), *static_cast<const E2*>(&b));
return v;
}
To provide some more detail/context; I have an array holding a bunch of values (could be of various types), and an arithmetic expression defining how I want to transform that array into another one. However, this transformation is not concrete, so I'm using the expression tree as something I can pass to objects that will then handle it in different ways (i.e. I have to wait to evaluate it). Additionally, I only want to define the expression once.
I'm wondering, if based on this design, I can introduce literals (without casting to an OpLiteral) to my expressions? For example:
double arr[100]{ 0 };
OpFrame arr_t(arr);
// I can do this
auto ev1 = arr_t + arr_t + arr_t + OpLiteral(2.0);
// But I would prefer to do this
auto ev2 = arr_t + arr_t + arr_t + 2.0;
Based on my question here, I know 2.0 won't automatically cast to the correct type, but the solution is also not compatible with this design (it causes either an ambiguous call, or in bigger expressions, mixes up the tree by applying the generic template rather than one based on OpExpression<T>).
How I had tried to implement that solution:
template<typename E1, typename T>
auto operator+(OpExpression<E1> const& a, T b)
{
auto v = OpFrameAdd<E1, OpLiteral<T>>(*static_cast<const E1*>(&a), OpLiteral<T>(b));
return v;
}
template<typename E2, typename T>
auto operator+(T a, OpExpression<E2> const& b)
{
auto v = OpFrameAdd<OpLiteral<T>, E2>(OpLiteral<T>(a), *static_cast<const E2*>(&b));
return v;
}
So my questions are:
Is it possible to augment the design to use the literal in the preferred way? If not, is this is just a limitation of the templates/design I chose? (Casting to OpLiteral is still a lot easier than making a new operator overload for each type and for both sides). More broadly, is there a known (different) design to deal with this problem? Have I applied this design correctly to the problem?
EDIT
With the given design, it doesn't appear possible to accept and convert another type implicitly. Ultimately, it seems the problem can be phrased as: I want to have an operation between 1) A parent class and another parent class; 2) A parent class an any other object; 3) Any class and that parent class. Obviously this is inherently problematic.
For instance, when I attempted to remedy the ambiguous call in the above attempt, it instead results in operations between children of OpExpression becoming a template argument of OpLiteral. This is because instead of resolving the 'correct' operator (i.e. applying operator with both argument types of OpExpression), it will choose the one with the more generic argument type.
The conclusion is that as it is, this is simply a limitation of the design (and for good reason).
However, suppose I have a complete list of all the literal types I would want to use in an expression. If I don't want to create individual template specializations for each one, how would I modify the overloaded operator to use that? Would I instead have to modify the classes of OpExpression?
I would like to know why implicit type conversion doesn't work with outside operator overloading on class templates. Here is the working, non-templated version:
class foo
{
public:
foo() = default;
foo(int that)
{}
foo& operator +=(foo rhs)
{
return *this;
}
};
foo operator +(foo lhs, foo rhs)
{
lhs += rhs;
return lhs;
}
As expected, the following lines compile correctly:
foo f, g;
f = f + g; // OK
f += 5; // OK
f = f + 5; // OK
f = 5 + f; // OK
On the other hand, when class foo is declared as a simple template like this:
template< typename T >
class foo
{
public:
foo() = default;
foo(int that)
{}
foo& operator +=(foo rhs)
{
return *this;
}
};
template< typename T >
foo< T > operator +(foo< T > lhs, foo< T > rhs)
{
lhs += rhs;
return lhs;
}
The following lines compile with errors:
foo< int > f, g;
f = f + g; // OK
f += 5; // OK
f = f + 5; // Error (no match for operator+)
f = 5 + f; // Error (no match for operator+)
I would like to understand why the compiler (GCC 4.6.2) is unable to perform implicit type conversion using the converting constructor for the template version of the class. Is that the expected behaviour? Apart from manually creating all the necessary overloads, is there any workaround for this?
The reason it does not just work is that implicit type conversions (that is, via constructors) do not apply during template argument deduction.
But it works if you make the outside operator a friend since then the type T is know, allowing the compiler to investigate what can be casted to make the arguments match.
I made an example based on yours (but removed C++11 stuff), inspired by Item 46 (a rational number class) in Scott Meyers Effective C++ (ed 3). Your question is almost an exact match to that item. Scott also notes that ... "this use of friend is not related to the access of non-public parts of the class."
This will also allow work with mixes of foo< T >, foo< U > etc as long as T and U can be added etc.
Also look at this post: C++ addition overload ambiguity
#include <iostream>
using namespace std;
template< class T >
class foo
{
private:
T _value;
public:
foo() : _value() {}
template <class U>
foo(const foo<U>& that) : _value(that.getval()) {}
// I'm sure this it can be done without this being public also;
T getval() const { return _value ; };
foo(const T& that) : _value(that) {}
friend const foo operator +(foo &lhs,const foo &rhs)
{
foo result(lhs._value+rhs._value);
return result;
};
friend const foo operator +(foo &lhs,const T &rhsval)
{
foo result(lhs._value+rhsval);
return result;
};
friend const foo operator +(const T &lhsval,foo &rhs)
{
foo result(lhsval+rhs._value);
return result;
};
friend foo& operator +=(foo &lhs,const foo &rhs)
{
lhs._value+=rhs._value;
return lhs;
};
friend std::ostream& operator<<(std::ostream& out, const foo& me){
return out <<me._value;
}
};
int main(){
foo< int > f, g;
foo< double > dd;
cout <<f<<endl;
f = f + g;
cout <<f<<endl;
f += 3 ;
cout <<f<<endl;
f = f + 5;
cout <<f<<endl;
f = 7 + f;
cout <<f<<endl;
dd=dd+f;
cout <<dd<<endl;
dd=f+dd;
cout <<dd<<endl;
dd=dd+7.3;
cout <<dd<<endl;
}
I put this question to the library authors at MS and got an extremely informative response from Stephan Lavavej, so I give him full credit for this information.
The compile error you get in the template case is due to the fact that template argument deduction runs before overload resolution, and template argument deduction needs exact matches to add anything to the overload set.
In detail, template argument deduction looks at each pair of parameter type P and argument type A, and tries to find template substitutions that will make A exactly match P. After finding matches for each argument, it checks for consistency (so that if you call bar(foo<T>, foo<T>) with T=int for the first parameter and T=double as the second, it also fails). Only after exact, consistent matches are successfully substituted in the function signature is that signature added to the set of candidate functions for overload resolution.
Only after all ordinary functions (found through name lookup) and matching function template signatures have been added to the overload set is overload resolution run, at which point all of these function signatures are evaluated for a "best match", during which time implicit conversions will be considered.
For the operator+(foo<T>, foo<T>) case with foo<int> + 5, template argument deduction can find no substitution for T that will make the expression foo<T> exactly match int, so that overload of operator+ gets tossed out as a candidate and the implicit conversion is never even seen.
The opinion here seems to be that this is generally a good thing, as it makes templates much more predictable, leaving the realm of strange implicit behaviors to overload resolution.
The standard has plenty to say about this at:
14.8.2.1 Deducing template arguments from a function call
"Template argument deduction is done by comparing each function template parameter type (call it P) with
the type of the corresponding argument of the call (call it A) as described below. ...
... In general, the deduction process attempts to find template argument values that will make the deduced A
identical to A (after the type A is transformed as described above)"
It goes on to list a few special cases where this rule has exceptions involving cv-qualifiers (so T& will be compatible with const T&), and matching of derived classes (it can in some cases match Derived& to Base&) but basically, exact matching is the rule.
All possible foo<T> are equally valid conversions from int since the constructor takes int, not the template type. The compiler isn't able to use the other parameter in the operator to guess which one you might mean, so you get the error. If you explicitly tell it which instantiation you want I believe it would work.