Funktioniert invoke_result_t für std::apply mit einem Tuple



  • Hi,

    für std::invoke kann ich mit std::invoke_result_t den Rückgabetyp ermitteln und zum Beispiel mit if constexpr in Abhängigkeit von dem return type unterschiedliche Dinge machen.

    Ein kurzer Test scheint zu ergeben, dass std::invoke_result_t auch funktioniert, wenn man eine Funktion mit std::apply aufruft und ein Tuple übergibt.

    Die Referenz von inovke_result sagt "Deduces the return type of an INVOKE expression at compile time."
    Die Referenz von std::apply sagt "Invoke the Callable object f with the elements of t as arguments."

    Macht das std::apply zu einer "invoke expression"? Funktioniert der Tuple Overload in meinem Beispiel dann immer?

    Hier das Beispiel, das mit Visual Studio (/std:c++latest) kompiliert und das erwartete zurück liefert :

    #include <print>
    #include <optional>
    
    template <typename R, typename F, typename ... Args> R DoInvoke(F&& f, Args&&... args)
    {
      using retType = std::invoke_result_t<F, Args...>;
      if constexpr (std::is_same_v<retType, std::optional<R>>)
      {
        return std::invoke(f, args...).value_or(0.0);
      }
      else 
      {
        return std::invoke(f, args...);
      }
    }
    
    //Overload for tuple
    template <typename R, typename F, typename ... Args> R DoInvoke(F&& f, std::tuple<Args...> t)
    {
      using retType = std::invoke_result_t<F, Args...>;
      if constexpr (std::is_same_v<retType, std::optional<R>>)
      {
        return std::apply(f, t).value_or(0.0);
      }
      else
      {
        return std::apply(f, t);
      }
    }
    
    int main() {
    
      auto res = DoInvoke<int>([](int a, int b) { return a + b; }, 1, 1);
    
      auto res2 = DoInvoke<double>([](double a, double b) { return b != 0.0 ? std::optional(a/b) : std::nullopt; }, 1, 0);
    
      auto res3 = DoInvoke<int>([](int a, int b) { return a + b; }, std::make_tuple(1, 1));
    
      auto res4 = DoInvoke<double>([](double a, double b) { return b != 0.0 ? std::optional(a / b) : std::nullopt; }, std::make_tuple(1, 0));
    
      std::println("{}", res );
      std::println("{}", res2);
      std::println("{}", res3);
      std::println("{}", res4);
    
      return 0;
    }
    


  • std::apply macht doch nichts anderes, als die Tuple-Elemente einzeln als Parameter an den Funktor zu übergeben. In deinem Test mit using retType = std::invoke_result_t<F, Args...> machst du ja exakt das gleiche (auch wenn du hier die value-category vernachlässigst), also warum sollte das nicht das richtige Ergebnis liefern?



  • Ach, danke für den Tritt in die richtige Richtung.

    Mein Gedanke war, das mit Tuple f(t) ja nicht funktioniert. Aber Args... für das Tuple oder die direkten Parameter ist ja identisch. Wenn das für die direkten Parameter funktioniert muss das auch für die Typen im Tuple funktionieren.


Anmelden zum Antworten