c++templatesc++14crtpexpression-templates

Use the return type of a method as an argument type of another method in a curiously recurring template class


Please consider the following code snippet:

template<class E>
class vector_expression
{
public:
    auto size() const {
        return static_cast<E const&>(*this).size();
    }

    auto operator[](/* type equal to E::size_type */ i) const
    {
        if (i >= size())
            throw std::length_error("");
        return static_cast<E const&>(*this)[i];
    }
}; // class vector_expression

template<typename T, class Tuple = std::vector<T>>
class vector
    : public vector_expression<vector<T, Tuple>>
{
public:
    using value_type = T;
    using size_type = typename Tuple::size_type;

    size_type size() const {
        return m_elements.size();
    }

    value_type operator[](size_type i) const { /* ... */ }

private:
    Tuple m_elements;
}; // class vector

The type of the argument i of vector_expression<E> should equal E::size_type. For a plausible reason, typename E::size_type doesn't work here. For the same reason, std::result_of_t<decltype(&size)(vector_expression)> doesn't work here.

So, how can we do it, if we can do it?


Solution

  • You can pass it explicitly as a template parameter to vector_expression:

    template<class E, class size_type>
    class vector_expression ...
    
    template<typename T, class Tuple = std::vector<T>>
    class vector
        : public vector_expression<vector<T, Tuple>, 
                                   typename Tuple::size_type> ...
    

    Edit:

    It is also possible to turn the problematic function into a member function template, so that it is not instantiated until the full class definition is seen:

    template <typename K = E>
    auto operator[](typename K::size_type i) const
    {
        if (i >= size())
            throw std::length_error("");
        return static_cast<K const&>(*this)[i];
    }