I am trying to create a method taking two instances of (different) specializations of a class template and returning the value of type deduced from template specializations at compile time.
What I have so far is (in minimal case)
#include <iostream>
#include <utility>
template<typename T> class box
{
protected:
T value;
public:
box(T value)
{
this->value = value;
}
friend std::ostream& operator<<(std::ostream& os, box<T>& obj)
{
return os << obj.value;
}
template<typename K> friend auto operator+(
const box<T>& left, const box<K>& right) ->
box<decltype(std::declval<T>() + std::declval<K>())>
{
typedef decltype(std::declval<T>() + std::declval<K>()) result_type;
return box<result_type>(left.value + right.value);
}
};
int main()
{
box<int> int_container = box<int>(2);
box<float> float_container = box<float>(7.0);
auto another_one = int_container + float_container;
std::cout << int_container << " " << float_container << " " << another_one << std::endl;
}
This won't compile (by gcc) until I make the field
value public. I suppose, the compiler cannot (or would not) treat container<K>
as a friend with the declared operator.
Of course, I could add a public getter for value
.
Other solution is to make all specializations of this template friends by adding template<typename> friend class box;
to the beginning of the class definition and making operator a class member instead of friend
. But that approach would not work in case I decide to create a friend
method for which no such alternative is present.
(Making all specializations friends without re-defining the operator as a class member does not compile for the same reason as in the original case.)
So is there a way to make a method template a friend of all of a class template's specializations in case its template parameter is used in one of its arguments' type definitions?
Since every operator needs to be a friend of every template specialization, you need to keep both template parameters free. I also found that you need to move the definition outside the class, though I'm not perfectly sure why:
template <typename T>
class box
{
// ...
template <typename A, typename B>
friend
auto operator+(const box<A>& left, const box<B>& right) -> box<decltype(std::declval<A>() + std::declval<B>())>;
};
template <typename A, typename B>
auto operator+(const box<A>& left, const box<B>& right) -> box<decltype(std::declval<A>() + std::declval<B>())>
{
return { left.value + right.value };
}
By the way, instead of the cumbersome auto -> decltype
construction, you could also use the simpler and more readable box<typename std::common_type<A, B>::type>
, or in C++14 box<std::common_type_t<A, B>>
.