#c #c 17 #metaprogramming
Вопрос:
Я пишу некоторые метафункции, и у меня возникла идея написать их с использованием C 17, если constexpr в сочетании с выведенным типом возвращаемого значения. Вот пример :
#include <tuple>
#include <type_traits>
#include <string>
template<typename TupleType, std::size_t I = 0, typename... ReturnData>
constexpr auto filterImpl()
{
if constexpr (I < std::tuple_size_v<TupleType>)
{
using Type = std::tuple_element_t<I, TupleType>;
if constexpr (std::is_arithmetic_v<Type>)
return filterImpl<TupleType, I 1, ReturnData..., Type>();
else
return filterImpl<TupleType, I 1, ReturnData...>();
}
else
return std::declval<std::tuple<ReturnData...>>();
}
template<typename Tuple>
using FilterTuple = decltype(filterImpl<Tuple>());
//std::tuple<int, float>
using MyTuple = FilterTuple<std::tuple<int, std::string, float, int*>>;
int main(int argc, char* argv[])
{
MyTuple t{ 3, 5.f };
return 0;
}
Конечно, вы, вероятно, могли бы использовать более простой шаблон для решения этой проблемы, но это всего лишь основной пример того, что я хочу сделать.
Этот код компилируется в msvc, но не в onlinegdb. Какой из них правильный? Разрешено ли использовать declval таким образом? Должен ли я вообще писать свои метафункции таким образом, или мне следует вернуться к классам шаблонов?
(Я думаю, что, если есть способ сделать это, более сложные метафункции могли бы быть легче прочитаны таким образом)
А Маквей:
#include <utility>
template<bool b>
constexpr auto evil()
{
if constexpr (b)
return 0;
else
return std::declval<int>();
}
template<bool b>
using Test = decltype(evil<b>());
int main()
{
Test<false> t{};
}
Комментарии:
1. допустимо ли иметь несогласованные типы возврата для типа
auto
возврата? потому что, похоже, вы это делаете, и я подумал, что это неверно.2. @Afshin это только те ветви
constexpr if
, которые отбрасываются3. @Afshin:
if constexpr
не отбрасывает операторы возврата, которые просто недоступны из-за предыдущего оператора возврата, но он удалит их поelse
мере необходимости.4. @Afshin подумайте о том, чтобы опубликовать это как вопрос. Код в комментариях чрезвычайно трудно читать. Я смотрел на него с минуту, чтобы понять, что на самом деле они разные. И в любом случае, комментарии не для вопросов и ответов, вот для чего нужны вопросы и ответы 😉
5. Это зависит от того, используется ли
std::declval
здесь odr. Что, в свою очередь, зависит от того, является ли это потенциально оцениваемым выражением. Я думаю, что это так: «Выражение потенциально оценивается,если только оно не является недооцененным операндом (пункт 8) или его подвыражением». Тело функции не является подвыражением выражения вызова функции. Поэтому я думаю, что на самомstd::declval<std::tuple<ReturnData...>>()
деле odr используется, когда шаблон функции создается таким образом, чтобы достичь этой ветви.
Ответ №1:
Поскольку вы должны создать экземпляр шаблона функции, чтобы определить ее тип возвращаемого значения, существует функция, которая использует odr std::declval<…>
, поэтому программа плохо сформирована. MSVC ошибочно принимает это (без предупреждения), хотя можно утверждать, что это правило не должно требовать диагностики, чтобы разрешить очевидную стратегию реализации, заключающуюся в том, чтобы просто не дать определения std::declval
.