I’ve found myself recently needing to handle passing and managing options across all of the components of our application.
I wanted all components to have their own options that could be managed externally.
Approach
I nest the options struct in the class and let it be qualified by the class name.
class MyComponent {
public:
  struct Options {
    struct {
      bool some_option{false};
      /* some other options */
    } MyComponent;
  };
  
  MyComponent() {}
  MyComponent(std::shared_ptr<Options>& options) :
      m_options{options} {}
  /* ... */
private:
  std::shared_ptr<Options> m_options{new Options};
};
We will devise a way to aggregate options and get a specific slice of options from it.
template <typename... TOptions>
struct OptionsAggregate : public TOptions... {
  using Type = OptionsAggregate<TOptions...>;
  template <typename TOptionType>
  auto Get() -> TOptionType& {
    static_assert(std::is_base_of_v<TOptionType, Type>,
      "Requested options are not a base of this aggregate");
    return static_cast<TOptionType&>(*this);
  }
};
So this variadic class template will take several options classes and inherit from them. It also has a Get method to get a slice of the aggregate.
We will have a ComponentManager that manages all of the components and their options.
template <typename TOptions>
class ComponentManager {
public:
  ComponentManager() {}
  ComponentManager(std::shared_ptr<MyComponent> component,
                   std::shared_ptr<Options> options) :
     m_options{std::move(options)} {}
  void Run();
private:
  std::shared_ptr<TOptions> m_options{new TOptions};
};
We can then use all of these things like the following:
struct GeneralOptions {
  bool debug{false};
};
using ComponentOptions = OptionsAggregate<GeneralOptions, MyComponent::Options>;
auto options = std::make_shared<ComponentOptions>();
options->Get<GeneralOptions>().debug = true;
/* set other options */
// the options that component can see are only a slice of the overall options
auto component = std::make_shared<MyComponent>(options);
ComponentManager manager{component, options};
A problem I discovered with this method appears when using the slicing syntax from ComponentManager methods which results in very unfortunate syntax. Imagine component manager had a method called Run:
template<typename TOptions>
void ComponentManager<TOptions>::Run() {
  auto& general_options = m_options->template Get<GeneralOptions>();
  /* use general_options */
}
This occurs because m_options depends on a template parameter as well as OptionsAggregate::Get.
Check Compiler Explorer for a full example.