Its hard to do this directly. This is because you need to use a combination of formatted(operator>>) and non-formatted(std::getline) input routines.
You want to use operator>> to read the id field (and correctly parse an integer); but then you also want to use the function std::getline(), using the third parameter '\t', to read a tab delimited field (Note: The field terminator defaults to '\n' line delimited values).
Normally you don't want to use mix the usage of operator>> and std::getline() together because of how they handle white space.
So the best solution is to write your own input operator and handle that extra space explicitly in a controlled manner.
How to do it:
I would create a class to represent the line.
struct Line
{
    int          id;
    std::string  col;
    std::string  uid;
    void swap(Line& other) noexcept {
        using std::swap;
        swap(id, other.id);
        swap(col, other.col);
        swap(uid, other.uid);
    }
    friend std::istream& operator>>(std::istream& in, Line& data);
};
Then you need to define in an input operator for reading the line.
std::istream& operator>>(std::istream& in, Line& data)
{
    Line   tmp;
    if (// 1 Read the id. Then disicard leading white space before second field.
        (linestream >> tmp.id >> std::ws) && 
        // 2 Read the second field (which is terminated by tab)
        (std::getline(tmp.col, linestream, '\t') &&
        // 3 Read the third field  (which is terminated by newline)
        (std::getline(tmp.uid, linestream)
        // I am being lazy on 3 you may want to be more specific.
       )
    {
        // We have correctly read all the data we need from
        // the line so set the data object from the tmp value.
        data.swap(tmp);
    }
    return in;
}
Now it can be used easily.
Line line;
while (infile >> line) {
    ### operations on row, col and uid ####
}