dimanche 1 mai 2016

Exception safety guarantee and std::move()

During some implementation I stuck with the problem about exception safety guarantee and using std::move(). I know that SO is not a good place to ask "what is your opinion" (class template Boo) kind of a question but here I would like to make sure if my understanding is correct and after you read this question maybe you can direct me to the right path.

Consider following code:

struct Foo {
    using Y = std::vector<std::string>;
    using X = std::vector<Y>;

    void add (Y y) {
        src.push_back (std::move (y));      // (1)
        src = process (std::move (src));    // (2)         
    }

private:
    X process (X&& vec) {                   // (F)
        for (auto& v : vec) {
            v.resize (10);                  // (3)
        }
        return std::move (vec);
    }


    X src;
};

Take a look at the add() member function, if an exception is thrown from (1) and according to the n4296 [vector.modifiers/1]

If an exception is thrown while inserting a single element at the end and T is CopyInsertable or is_nothrow_move_constructible<T>::value is true, there are no effects.

then strong guarantee is preserved, am I right?

If we look at the (2) then exception can also be thrown here, because (3) can throw an exception. Correct me if I am wrong: In this situation (2) if I move src to the process() member function and exception is thrown from process() then src is unspecified and that means that only basic guarantee is preserved?

To make add() member function strong guarantee should I change (2) and (F) respectively to:

src = process (src); // (2)
X process (X vec);   // (F)

?

So if Foo::src has huge number of elements, then making strong guarantee is associated with less efficiency (because extra copy has to be made) ?

Maybe guarantee level should be left to decide by the user of the class, by making it a class template, similar to this:

struct S {};
struct B {};

template <typename T>
struct Boo {
    using Y = std::vector<std::string>;
    using X = std::vector<Y>;

    void add (Y y) {
        src.push_back (std::move (y));
        src = process_helper (typename std::is_same<T, S>::type {});
    }


private:
    X process_helper (std::true_type) {
        return process (src);
    }

    X process_helper (std::false_type) {
        return process (std::move (src));
    }

    X process (X vec) {
        for (auto& v : vec) {
            v.resize (10);                  
        }
        return std::move (vec);
    }

    X src;
};

Is it a good or bad idea?

Aucun commentaire:

Enregistrer un commentaire