Есть ли способ решить, можно ли что-то оценить с помощью 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/
Но что, если тест в if constexpr([test]) нельзя оценить во время компиляции?
Если это невозможно оценить во время компиляции, то что бы вы сделали компилятору, кроме ошибки или оценки во время выполнения?
Если его можно оценить во время компиляции, должно быть выполнено первое тело. В противном случае должно быть выполнено второе тело среды выполнения. В настоящее время я изучаю рекомендацию 0x5453.
@AartStuurman: Что такое do_stuff, что он может работать во время компиляции или во время выполнения, но сам по себе не должен constexpr? Не лучше ли просто сделать ее функцией constexpr и передать ей значение get_data в качестве параметра?
Тем временем я действительно пересмотрел свой выбор дизайна, но, тем не менее, я думаю, что это интересный вопрос :)





Не совсем то, о чем вы просили (я разработал свойство пользовательского типа, специфичное для статического метода 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)"
}
Это безумие, это использование оператора запятой, перегрузка long/int... Проголосуйте. :/
@matovitch - никогда не недооценивайте силу оператора запятой }:-)
Будет ли это работать на платформах, где sizeof(long) равно sizeof(int)?
@ГрегориНисбет - Да. Потому что для языка, как и для компилятора, они остаются разными типами.
Вот еще одно решение, которое является более общим (применимым к любому выражению, без определения каждый раз отдельного шаблона).
В этом решении используется то, что (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());
}
}
Интригующее решение... таким образом вы получите тот же результат моих пользовательских признаков типа, но более синтетический, и, прежде всего, точное проверенное выражение (base::get_data()) встроено в аргумент, а не жестко запрограммировано, как в моем решении. Очень хорошо. Я должен помнить это.
Я принимаю это, потому что это ответ на общий случай вопроса. Ответ max66 также очень полезен (в случаях, отличных от С++ 2a), но требует повторения для каждого использования :)
Запятая оператор SFINAE... мой разум идет БУМ.
Просветительское решение проблемы. Я провел некоторые тесты по этому поводу и считаю, что использование запятой SFINAE не требуется, и я почти уверен, что шаблон формы template<typename T, auto = T()() >, где мой T — ваш Lambda, будет в равной степени достаточным.
Если подумать, запятая SFINE необходима из-за возвращаемого типа лямбда void. (в своем тестировании я использовал лямбду, похожую на []{ base::get_data(); return true;}, которая всегда не пуста.
Хм, тело
if constexprбудет оцениваться только в том случае, если выражение вif constexprистинно во время компиляции. Это то, что вы ищете?