C ++ проверяет, может ли оператор быть оценен constexpr - PullRequest
19 голосов
/ 21 марта 2019

Существует ли метод, позволяющий решить, может ли что-то быть оценено constexpr, и использовать результат в качестве логического значения constexpr?Мой упрощенный вариант использования следующий:

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());
        }
    }
}

Моя цель - C ++ 2a.

Я нашел следующую ветку reddit, но я не большой поклонник макросов.https://www.reddit.com/r/cpp/comments/7c208c/is_constexpr_a_macro_that_check_if_an_expression/

Ответы [ 3 ]

17 голосов
/ 22 марта 2019

Вот еще одно решение, которое является более общим (применимо к любому выражению, без определения отдельного шаблона каждый раз).

Это решение использует то, что (1) лямбда-выражения могут быть constexpr, начиная с C ++ 17 (2) тип лямбда-захвата без захвата является конструируемым по умолчанию, начиная с C ++ 20.

Идея состоит в том, что перегрузка, которая возвращает true, выбирается тогда и только тогда, когда Lambda{}() может появляться в аргументе шаблона, что фактически требует, чтобы лямбда-вызов был постоянным выражением.

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());
    }
}
11 голосов
/ 22 марта 2019

Не совсем то, что вы спросили (я разработал специальную черту типа, специфичную для get_value() статического метода ... возможно, это можно обобщить, но на данный момент я не знаю как), но я полагаю,Вы можете использовать SFINAE и сделать что-то следующим образом

#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)"
 }
7 голосов
/ 22 марта 2019
template<auto> struct require_constant;
template<class T>
concept has_constexpr_data = requires { typename require_constant<T::get_data()>; };

Это в основном то, что используется std::ranges::split_view.

...