Design of Experiments data structure with C++17
Because you're defining your member functions within the class declaration, you don't need to use the inline keyword. All member functions declared in the class declaration are implicitly inline (including the default and copy constructors) are inline.
In your copy constructor, you can make use of member initializers to construct the copied maps directly, rather than default construct and assign:
doe_model(const doe_model&amp; doe): required_explorations(doe.required_explorations),
    next(required_explorations.end()), number_of_explorations(doe.number_of_explorations)
}

There are a few places where you're using two statements but they can be combined.
In update_config, you can decrement in the test:
if (--number_of_explorations.at(config_id) &lt;= 0)

In get_next, you can combine the iterator increment and return statement:
return ++next;

This also uses the preincrement on the iterator, as it avoids creating a copy of the original iterator that is returned by the postincrement version, then immediately discarded.
In add_config, you have the potential of adding a config with a higher number of required explorations than are provided if that config already exists (if required_explorations is assigned to, no change is made to number_of_explorations). This may or may not be a problem.
You should consider adding a move constructor and a move assignment operator, which can avoid creating copies.