C++ check if-Anweisung kann constexpr ausgewertet werden

Lesezeit: 5 Minuten

Benutzer-Avatar
Aart Stuurmann

Gibt es eine Methode, um zu entscheiden, ob etwas constexpr ausgewertet werden kann, und das Ergebnis als constexpr boolean zu verwenden? Mein vereinfachter Anwendungsfall ist wie folgt:

template <typename base>
class derived
{
    template<size_t size>
    void do_stuff() { (...) }

    void do_stuff(size_t size) { (...) }
public:
    void execute()
    {
        if constexpr(is_constexpr(base::get_data())
        {
            do_stuff<base::get_data()>();
        }
        else
        {
            do_stuff(base::get_data());
        }
    }
}

Mein Ziel ist C++2a.

Ich habe den folgenden reddit-Thread gefunden, aber ich bin kein großer Fan der Makros. https://www.reddit.com/r/cpp/comments/7c208c/is_constexpr_a_macro_that_check_if_an_expression/

  • Hmm, der Körper von a if constexpr wird nur ausgewertet, wenn der Ausdruck in der if constexpr ist zur Kompilierzeit wahr. Ist es das, was Sie suchen?

    – Jesper Juhl

    21. März 2019 um 20:13 Uhr

  • Aber was ist, wenn der Test in if constexpr([test]) ist zur Kompilierzeit nicht auswertbar?

    – Aart Stuurmann

    21. März 2019 um 20:17 Uhr

  • Vielleicht kannst du etwas damit anfangen std::is_constant_evaluated?

    – 0x5453

    21. März 2019 um 20:18 Uhr

  • de.cppreference.com/w/cpp/language/if

    – Jesper Juhl

    21. März 2019 um 20:18 Uhr

  • @AartStuurman: Was ist do_stuff dass es zur Kompilierungs- oder Laufzeit ausgeführt werden kann, aber selbst nicht ausgeführt werden sollte constexpr? Wäre es nicht sinnvoller, es einfach zu machen constexpr Funktion, und übergeben Sie ihr den Wert von get_data als Parameter?

    – Nicol Bolas

    21. März 2019 um 20:38 Uhr

Benutzer-Avatar
cpplerner

Hier ist eine andere Lösung, die allgemeiner ist (anwendbar auf jeden Ausdruck, ohne jedes Mal eine separate Vorlage zu definieren).

Diese Lösung nutzt, dass (1) Lambda-Ausdrücke ab C++17 constexpr sein können (2) der Typ eines erfassungslosen Lambda ab C++20 standardmäßig konstruierbar ist.

Die Idee ist, die Überlastung, die zurückkehrt true wird ausgewählt wann und nur wann Lambda{}() kann innerhalb eines Vorlagenarguments erscheinen, was effektiv erfordert, dass der Lambda-Aufruf ein konstanter Ausdruck ist.

template<class Lambda, int=(Lambda{}(), 0)>
constexpr bool is_constexpr(Lambda) { return true; }
constexpr bool is_constexpr(...) { return false; }

template <typename base>
class derived
{
    // ...

    void execute()
    {
        if constexpr(is_constexpr([]{ base::get_data(); }))
            do_stuff<base::get_data()>();
        else
            do_stuff(base::get_data());
    }
}

  • Faszinierende Lösung … auf diese Weise erhalten Sie das gleiche Ergebnis meiner benutzerdefinierten Typmerkmale, aber synthetischer und vor allem den genauen verifizierten Ausdruck (base::get_data()) ist in das Argument eingebettet und nicht wie in meiner Lösung fest codiert. Sehr schön. Ich muss es mir merken.

    – max66

    22. März 2019 um 0:24 Uhr


  • Ich akzeptiere dies, weil es eine Antwort auf den allgemeinen Fall der Frage ist. max66-Antwort ist auch sehr nützlich (in Nicht-C ++ 2a-Fällen), erfordert jedoch eine Wiederholung für jede Verwendung 🙂

    – Aart Stuurmann

    22. März 2019 um 9:42 Uhr

  • Komma-Operator SFINAE … mein Verstand macht BOOM.

    – Biomann

    24. Oktober 2021 um 8:02 Uhr

  • Eine erhellende Lösung des Problems. Ich habe diesbezüglich einige Tests durchgeführt, und ich glaube, dass die Verwendung des Kommas SFINAE unnötig ist, und ich bin mir ziemlich sicher, dass eine Vorlage des Formulars vorhanden ist template<typename T, auto = T()() >wo meine T ist dein Lambda würde auch reichen.

    – Benutzer2628206

    17. Januar um 7:17 Uhr


  • Bei näherer Betrachtung wird das Komma SFINE aufgrund des void-Rückgabetyps von Lambda benötigt. (In meinen Tests habe ich ein Lambda verwendet, das verwandt ist mit []{ base::get_data(); return true;}die immer nicht leer ist.

    – Benutzer2628206

    17. Januar um 19:24 Uhr


Benutzer-Avatar
max66

Nicht genau das, was Sie gefragt haben (ich habe ein benutzerdefiniertes Typmerkmal speziell für a entwickelt get_value() statische Methode … vielleicht ist es möglich, es zu verallgemeinern, aber im Moment weiß ich nicht wie), aber ich nehme an, Sie können SFINAE verwenden und etwas wie folgt machen

#include <iostream>
#include <type_traits>

template <typename T>
constexpr auto icee_helper (int)
   -> decltype( std::integral_constant<decltype(T::get_data()), T::get_data()>{},
                std::true_type{} );

template <typename>
constexpr auto icee_helper (long)
   -> std::false_type;

template <typename T>
using isConstExprEval = decltype(icee_helper<T>(0));

template <typename base>
struct derived
 {
   template <std::size_t I>
   void do_stuff()
    { std::cout << "constexpr case (" << I << ')' << std::endl; }

   void do_stuff (std::size_t i)
    { std::cout << "not constexpr case (" << i << ')' << std::endl; }

   void execute ()
    {
      if constexpr ( isConstExprEval<base>::value )
         do_stuff<base::get_data()>();
      else
         do_stuff(base::get_data());
    }
 };

struct foo
 { static constexpr std::size_t get_data () { return 1u; } };

struct bar
 { static std::size_t get_data () { return 2u; } };

int main ()
 { 
   derived<foo>{}.execute(); // print "constexpr case (1)"
   derived<bar>{}.execute(); // print "not constexpr case (2)"
 }

  • Das ist Wahnsinn, diese Verwendung des Komma-Operators, die Long/Int-Überladung … Haben Sie eine positive Stimme. :/

    – Matowitsch

    21. März 2019 um 21:13 Uhr

  • @matovitch – unterschätze niemals die Macht des Komma-Operators }:‑)

    – max66

    21. März 2019 um 21:16 Uhr

  • Funktioniert dies auf Plattformen, wo sizeof(long) ist gleich sizeof(int)?

    – Greg Nisbet

    22. März 2019 um 3:35 Uhr

  • @GregoryNisbet – Ja. Denn für die Sprache und den Compiler bleiben sie unterschiedliche Typen.

    – max66

    22. März 2019 um 8:08 Uhr

template<auto> struct require_constant;
template<class T>
concept has_constexpr_data = requires { typename require_constant<T::get_data()>; };

Dies ist im Grunde das, was von verwendet wird std::ranges::split_view.

1015630cookie-checkC++ check if-Anweisung kann constexpr ausgewertet werden

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy