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?
 
                        
You can pass it explicitly as a template parameter to
vector_expression: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: