c++compiler-warnings

How to initialize an enum when I do not know the exact type


My program contains a template function like this:

    template<typename Key> Key getChoiceWidgetSelection(wxChoice& choiceWidget, bool *ok) {
        Key ret;
        *ok = false;
        void *clientdata = choiceWidget.GetClientData( choiceWidget.GetSelection() );
        if ( clientdata != nullptr ) {
            *ok = true;
            ret = *static_cast<Key *>(clientdata);
        }
        return ret;
    }

Apart from Key being an enumeration type, nothing can be said about it. ok will be true if and only if ret will be assigned. This is why the compiler yields a warning that ret may be uninitialized.

How can I prevent this compiler warning?


Solution

  • Just wrapping up my and others' comments in an answer, and adding some examples;

    1. (@christianstieber's and @3CEZVQ's comments): just assign a default 'null' value to it, even if that isn't a valid value for that enum, since no-one is going to use it anyway like you assert.
      Example:

      template <typename Key, Key defaultValue> Key getChoiceWidgetSelection(wxChoice& choiceWidget, bool *ok) {
          Key ret{};
          *ok = false;
          void *clientdata = choiceWidget.GetClientData( choiceWidget.GetSelection() );
          if ( clientdata != nullptr ) {
              *ok = true;
              ret = *static_cast<Key *>(clientdata);
          }
          return ret;
      }
      
      void DoIt() {
          wxChoice choice = ...;
          bool ok;
          wxType ret = getChoiceWidgetSelection<wxType>(choice, &ok);
          if (ok) {
              // use ret
          }
      }
      
    2. (@Jarod42's comment): bundle the enum with the bool in a std::optional<Key>.
      Example:

      #include <optional>
      
      template <typename Key> std::optional<Key> getChoiceWidgetSelection(wxChoice& choiceWidget) {
          std::optional<Key> ret;
          void *clientdata = choiceWidget.GetClientData( choiceWidget.GetSelection() );
          if ( clientdata != nullptr ) {
              ret = *static_cast<Key *>(clientdata);
          }
          return ret;
      }
      
      void DoIt() {
          wxChoice choice = ...;
          std::optional<wxType> ret = getChoiceWidgetSelection<wxType>(choice);
          if (ret) {
              // use *ret
          }
      }
      
    3. I don't know if this is possible in your case, but one way is to add a 2nd template value argument for the zero value that the caller must supply. Might be a bit much to type all the time though.
      Example:

      template <typename Key, Key defaultValue> Key getChoiceWidgetSelection(wxChoice& choiceWidget, bool *ok) {
          Key ret = defaultValue;
          *ok = false;
          void *clientdata = choiceWidget.GetClientData( choiceWidget.GetSelection() );
          if ( clientdata != nullptr ) {
              *ok = true;
              ret = *static_cast<Key *>(clientdata);
          }
          return ret;
      }
      
      void DoIt() {
          wxChoice choice = ...;
          bool ok;
          wxType ret = getChoiceWidgetSelection<wxType, wxType::someMinValue>(choice, &ok);
          if (ok) {
              // use ret
          }
      }
      
    4. Another solution, if you have control over the enums (which I guess you don't since I suspect you insert WX enums here) is to add an artificial extra named value that mirrors the 'min' value. Since this function is a template it can resolve the value just fine using that fixed label.
      Example:

      enum class MyEnum {
          a,
          b,
          c,
          min = a
      };
      
      template <typename Key> Key getChoiceWidgetSelection(wxChoice& choiceWidget, bool *ok) {
          Key ret = Key::min;
          *ok = false;
          void *clientdata = choiceWidget.GetClientData( choiceWidget.GetSelection() );
          if ( clientdata != nullptr ) {
              *ok = true;
              ret = *static_cast<Key *>(clientdata);
          }
          return ret;
      }
      
      void DoIt() {
          wxChoice choice = ...;
          bool ok;
          MyEnum ret = getChoiceWidgetSelection<MyEnum>(choice, &ok);
          if (ok) {
              // use ret
          }
      }
      
    5. Another alternative set-up: make the function return the 'ok' bool, and have it have an out argument for the enum instead. Then the caller can set the enum to a good/appropriate default beforehand (or not), and you can then also incorporate the function's return value directly in the if which decides whether to use the returned enum value; might make for a nicer code flow too. Plus you now have the enum as part of the function's argument list, so the compiler can deduce the template specialization to use on its own.
      Example:

      template <typename Key, Key defaultValue> bool getChoiceWidgetSelection(wxChoice& choiceWidget, Key* ret) {
          bool ok = false;
          void *clientdata = choiceWidget.GetClientData( choiceWidget.GetSelection() );
          if ( clientdata != nullptr ) {
              ok = true;
              *ret = *static_cast<Key *>(clientdata);
          }
          return ok;
      }
      
      void DoIt() {
          wxChoice choice = ...;
          wxType ret;
          if (getChoiceWidgetSelection(choice, &ret)) {
              // use ret
          }
      }
      

    I'm inclined to use #5 out of habit, though I should embrace #2's `std::optional` more now that the main code base I'm working on has been upgraded to C++ 2017.