Digit-by-digit addition using vectors
I'm not sure whether you used using namespace std; in the header, or only committed the lesser evil of needlessly importing many names into the global namespace in a header. Anyway, as you are implementing some new library-code, open your own namespace and confine your code to it.
Please read Why is “using namespace std;” considered bad practice?


Let's take a look at your data-structure:


You are using a std::vector, so you have its re-allocation-behavior of roughly doubling the space on demand, and keeping the maximum ever reserved. Not a bad choice.
You store your number as single digit characters. That's workable, but you should really think about storing 0 as 0 not '0'. It makes math easier. Still, I'll keep with that.
Binary operators should, for those where the language allows it, be free functions or at least friend-functions to reduce coupling and enable conversion on the left hand side.
That holds even if you later add overloads to remove those conversions for efficiency.
If you provide some binary operator, it's convenient and customary to also provide the corresponding compound-assignment-operator.


And now your interface:


A conversion (single-argument-ctor or operator Type) should only be implicit if it is lossless and is expected to succeed for all valid inputs.
But "A" is a valid string but will not be accepted.
Use std::string_view instead of constant reference to std::string as input. Thus the caller isn't forced to use a std::string with the possible dynamic allocation (and copy) that entails.


Let's take a look at your ctor:


How does DEBUGF work? Maybe it's a debug-tracing-macro, but what's the first argument for then? Oh well, it's not that important.
You know exactly how long the input is, so why don't you .reserve() space for it to avoid reallocations?
You know calling std::isdigit() with a negative value != EOF is undefined behavior? Cast to unsigned char first!
You optimized your ctor for bad input, as you do a first pass solely to verify the input, and then a second pass to finally store it. Combine the passes into one.
You should probably eliminate leading zeroes. That also makes everything easier.




ubigint::ubigint(std::string_view that) {
    auto pos = that.find_first_not_of('0');
    if(pos != that.npos)
        that = that.substr(pos);

    ubig_value.reserve(that.size());

    for(auto it = rbegin(that); it != rend(that); ++it) {
        if(!isdigit((unsigned char)*it))
            throw invalid_argument ("ubigint::ubigint(" + that + ")");
        ubig_value.push_back((unsigned char)*it);
    }
}


Now, your addition:


While it is often recommended to implement operator+ as a free function in terms of operator+= to avoid repetition, your class doesn't qualify, as it would result in (maybe severe) loss of efficiency. You might want to do it the other way around instead.
Still, that doesn't mean that your try is good. Your first loop repeatedly tests both inputs lengths. Why don't you use std::max(a, b)?
You are using / and % to calculate the carry and the value. While it works, it's highly inefficient. You know the carry is at most one, so use an if-statement and subtraction.
Point 2 comes back to bite you there. See how biasing the numbers with '0' creeps everywhere into the calculations?
The following if-else if-statement is just painful. You are doing the same for both cases, just with a different input-variable. Merge that.
The end really gets me scratching my head. Why do you convert the perfectly usable ubigint into a std::string and then back on returning?
I haven't seen .size() and operator[] in your interface, but adding them is a good idea, so I pretend you already did so. You might want to overhaul your data-structure first though, to be 0 instead of '0'-based.




ubigint operator+ (const ubigint&amp; a, const ubigint&amp; b) {
    if(a.size() &gt; b.size())
        return b + a;

    bool carry = false;
    ubigint result;
    result.reserve(b.size() + 1);
    std::size_t i = 0;
    for(; i &lt; a.size(); ++i) {
        auto c = a[i] + b[i] + carry - '0';
        if(c &lt;= '9') {
            carry = false;
        } else {
            c -= 10;
            carry = true;
        }
        result.push_back(c);
    }

    while(carry &amp;&amp; i &lt; b.size()) {
        auto c = b[i] + carry;
        if(c &lt;= '9')
            carry = false;
        else
            c -= 10;
        result.ubig_value.push_back(c);
    }

    if(!carry)
        std::copy(b.begin() + i, b.end(), std::back_inserter(result));
    else
        result.ubig_value.push_back(c);
    return result;
}