Pregunta

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?

¿Fue útil?

Solución

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>>.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top