Edit: I just noticed that within my CSV file, the iterator does work only when there is a newline as the last line in the file. Does this have to do with istream and not my implementation of this iterator?
I have the following class and iterator: For some reason, when using a for loop to iterate through, the loop exits immediately and doesn't execute any code within the loop. However, when I call get_row() manually, it works as intended.
template<typename ...ColType>
class CSV
{
public:
    using value_type = std::tuple<ColType...>;
    class iterator;
    explicit CSV(std::istream &in, const char delimiter = ',') : in_(in), delimiter(delimiter)
    {
    }
    std::tuple<ColType...> get_row()
    {
        auto next = std::stringstream(next_valid_line());
        std::tuple<ColType...> tuple;
        parse_row<>(next, tuple);
        return tuple;
    }
    [[nodiscard]] inline bool good() const { return in_.good(); }
    inline iterator begin() { return iterator(*this); }
    inline iterator end() { return iterator(); }
private:
    std::istream &in_;
    const char delimiter;
    [[nodiscard]] std::string next_valid_line() const
    {
        std::string line;
        while (std::getline(in_, line))
        {
            if (!line.empty() && !isspace(line.front()) && line.rfind(COMMENT_DELIMITER, 0) != 0)
            {
                return line;
            }
        }
        return "";
    }
    template<std::size_t Index = 0>
    void parse_row(std::stringstream &s, std::tuple<ColType...> &tuple)
    {
        if constexpr (sizeof...(ColType) > Index)
        {
            std::string col;
            std::getline(s, col, delimiter);
            trim(col);
            std::stringstream tmp(col);
            tmp >> std::get<Index>(tuple);
            parse_row<Index + 1>(s, tuple);
        }
    }
    static void trim(std::string &s)
    {
        const auto is_space = [](char c)
        { return !std::isspace(c); };
        s.erase(s.begin(), std::find_if(s.begin(), s.end(), is_space));
        s.erase(std::find_if(s.rbegin(), s.rend(), is_space).base(), s.end());
    }
};
template<typename... ColType>
class CSV<ColType...>::iterator
{
public:
    using iterator_category = std::input_iterator_tag;
    using value_type        = typename CSV::value_type;
    using difference_type   = std::size_t;
    using pointer   = typename CSV::value_type *;
    using reference = typename CSV::value_type &;
    iterator() : csv_(nullptr)
    {
    }
    explicit iterator(CSV &other) : csv_(other.good() ? &other : nullptr)
    {
        ++(*this);
    }
    inline iterator &operator++()
    {
        if (csv_ != nullptr)
        {
            row_ = csv_->get_row();
            if (!csv_->good()) { csv_ = nullptr; }
        }
        return *this;
    }
    inline iterator operator++(int i)
    {
        iterator tmp = *this;
        ++(*this);
        return tmp;
    }
    inline value_type const &operator*() const { return row_; }
    inline value_type const *operator->() const { return &row_; }
    inline bool operator==(iterator const &other)
    {
        return (this == &other) || (csv_ == nullptr && other.csv_ == nullptr);
    }
    inline bool operator!=(iterator const &other)
    {
        return !(*this == other);
    }
private:
    typename CSV::value_type row_;
    CSV *csv_;
};
The iterator class is essentially a copy of https://github.com/LizardM4/ballin-octo-tribble/blob/master/csv/csv.h , so I'm confused as to why mine doesn't work. I'm somewhat experienced with C++, however not with iterators.
From what I can tell, csv_ is being erroneously set to nullptr, possibly because of in_.good() returning false for some reason? I can't think of anything else. Thanks.
