Project Euler 18/67: Maximum Sum from Top to Bottom of the Triangle
Generally speaking, I find that your code is clear. I just have a few remarks:


You should order your headers in alhabetical order. It will help you to quickly check whether some header is already included or not in you plan to add some:

#include &lt;fstream&gt;
#include &lt;iostream&gt;
#include &lt;sstream&gt;
#include &lt;string&gt;
#include &lt;vector&gt;

You should open your file directly from the constructor, and check whether there was a problem when you tried to open the file:

std::ifstream nums.open("triangle.txt");
if (not nums.good())
{
    throw std::runtime_error("Could not open the file.");
}

You can replace this condition:

if(grid[a][b] &gt; grid[a][b+1]){
    grid[a-1][b] += grid[a][b];
}
else{
    grid[a-1][b] += grid[a][b+1];
}


You can use std::max instead:

grid[a-1][b] += std::max(grid[a][b], grid[a][b+1]);

I don't know why you chosed , as a separator since the original file uses a space as separator. Had you kept the space as a separator in your file, you vould have rewritten the function split with std::istream_iterator, with an int template parameter:

std::vector&lt;int&gt; split(const std::string&amp; line){
    std::stringstream ss(line);
    return {
        std::istream_iterator&lt;int&gt;{ss},
        std::istream_iterator&lt;int&gt;{}
    };
}


The braces after the return correspond to list initialization. The rules for list initialization are pretty complex; the curly braces after return mean that an instance of the anounced return type (std::vector&lt;int&gt;) should be created with the arguments between the braces. In our case, the chosen constructor is the one that matches the best two istream_iterator&lt;int&gt; instances:

template&lt;class InputIt&gt;
vector(InputIt first, InputIt last,
       const Allocator&amp; alloc=Allocator());