#c #visual-studio-2017 #c 17 #constexpr #template-auto
#c #visual-studio-2017 #c 17 #constexpr #шаблон-авто
Вопрос:
Я попытался создать мета-поиск индекса для значений в списках значений.
это код:
#include<type_traits>
#include<utility>
template <auto... Values>
struct ValueTplList;
template <auto>
constexpr int MetaFindV(int ind)
{ // not found case
return -1;
}
template <auto NeedleV, auto V, auto... Vs>
constexpr int MetaFindV(int ind = 0)
{
if constexpr (std::is_same_v<decltype(NeedleV), decltype(V)>)
{
return NeedleV == V ? ind : MetaFindV<NeedleV, Vs...>(ind 1);
}
else
{
return MetaFindV<NeedleV, Vs...>(ind 1);
}
}
// main
template <auto, typename>
struct MetaIndexOfV;
// destructurer
template <auto V, template<auto...> class Tmpl, auto... Vs>
struct MetaIndexOfV<V, Tmpl<Vs...> >
: std::integral_constant< int, MetaFindV<V, Vs...>() >
{};
// template variable version of it:
template <auto V, typename TemplInst>
constexpr int metaFindV_v = MetaIndexOfV<V, TemplInst>::value;
// tests
static_assert(metaFindV_v< 0, ValueTplList<0> > == 0 );
static_assert(metaFindV_v< 5, ValueTplList<0> > == -1);
static_assert(metaFindV_v< 0, ValueTplList<nullptr, 0> > == 1);
static_assert(metaFindV_v< 2, ValueTplList<1, (long)2, 2, 3, 4> > == 2);
static_assert(metaFindV_v< -1, ValueTplList<-2, -1, 42> > == 1);
найдите поведение здесь:
https://godbolt.org/z/ukwxpN
Вы обнаружите, что это отлично компилируется с gcc 7
и выше, а также с clang 5
и выше.
Интересно, действительно ли я сделал что-то не так по стандарту, который предписывает такую ненависть MSVC. Я просто не могу видеть это сам прямо сейчас:'(
В нем говорится:
(30): ошибка C2672: ‘MetaFindV’: не найдена соответствующая перегруженная функция > (31): примечание: смотрите ссылку на компиляцию экземпляра шаблона класса ‘MetaIndexOfV>’ (30): ошибка C2975: ‘V’: недопустимый аргумент шаблона для ‘MetaFindV’, ожидаемое выражение константы времени компиляции (12): примечание: смотрите объявление ‘V’ (30): ошибка C2977: ‘MetaFindV’ : слишком много аргументов шаблона
Я думаю, что это жалоба в этой строке
: std::integral_constant< int, MetaFindV<V, Vs...>() >
Например, если MetaFindV<V, Vs...>()
не было соответствующей функции.
(конечное примечание: если вы измените все auto
на typename
, — с некоторыми небольшими корректировками — все это начнет работать (но, конечно, только для типов и списка типов), даже если шаблон кода точно такой же.)
РЕДАКТИРОВАТЬ: по словам какого-то бота, команда VS теперь исправила это. (Хотя я все еще не нахожу ее в примечании к выпуску) https://developercommunity.visualstudio.com/t/template-constexpr-function-not-found-from-templat/490679
Комментарии:
1. Я подозреваю плохую поддержку C 17 для MSVC; в любом случае я предлагаю альтернативный способ написания тела рекурсивной версии
MetaFindV()
: используя тот факт, чтоNeedleV
иV
известны во время компиляции, вы можете перевестиNeedleV == V
тест внутриif constexpr
теста и упростить следующим образом:if constexpr ( std::is_same_v<decltype(NeedleV), decltype(V)> amp;amp; NeedleV == V ) return ind; else return MetaFindV<NeedleV, Vs...>( ind);
. Я нахожу этот способ более понятным и элегантным (очевидно, ИМХО).2. Я попробовал вашу идею в godbolt (вы тоже можете). По-прежнему не удается. Я нашел решение, отказавшись от списков значений и выполнив в стиле C 03: перенос значений в типы блоков. И даже я заставил ее в основном работать, MSVC все еще выполняет дрянное
is_same
распознаваниеintegral_constants
типаlong
иint
с тем же значением, как если бы они были одинаковыми. Что означает, что всеstatic_assert
проходят ожидаемый предпоследний (с3
, заменить3
на2
и==2
в любом случае это было первоначальным намерением теста. Я отредактирую это. И что еще более безумно: intellisense выполняет static_assert лучше, чем компилятор.3. Извините, но мое предложение не было ответом на ваш вопрос: я думаю, что ваш код правильный и что MSVC прослушивается. Мое предложение состояло в том, чтобы написать ваш код более понятным, но без решения проблемы MSVC.
4. MSVC — это куча вонючего дерьма. Я не могу больше согласиться с мнением этого парня из reddit reddit.com/r/cpp/comments/746x7b/this_is_why_i_hate_msvc это просто никогда не работает корректно
5. @max66 О, хорошо для предложения. Я не хотел использовать
amp;amp;
, потому что я не уверен, что я был защищен эквивалентностью типов, поскольку мы еще не вошли в телоif constexpr
, другими словами, я не уверен, что существует отложенная оценкаamp;amp;
на уровне constexpr. Вы знаете? Если это не сработает, мы получим ошибки сборки при попытке сравнить указатели с числами с плавающей точкой или чем-то еще…
Ответ №1:
Не ответ (извините: я подозреваю, что это ошибка MSVC, но я не уверен), а длинный комментарий.
Я предлагаю совершенно другой способ получить то, что вы хотите:
#include <type_traits>
#include <string>
template <auto... Values>
struct ValueTplList
{ };
template <auto, auto>
struct strongSame : public std::false_type
{ };
template <auto A>
struct strongSame<A, A> : public std::true_type
{ };
template <auto TargetVal, auto ... Values>
constexpr int foo (ValueTplList<Values...> const amp;)
{
int ind = -1;
(void)( ( ind, strongSame<TargetVal, Values>::value)
|| ... || ( ind, true) );
return std::size_t(ind) == sizeof...(Values) ? -1 : ind;
}
template <auto V, typename TemplInst>
constexpr int metaFindV_v = foo<V>(TemplInst{});
// tests
static_assert(metaFindV_v< 0, ValueTplList<0> > == 0 );
static_assert(metaFindV_v< 5, ValueTplList<0> > == -1);
static_assert(metaFindV_v< 0, ValueTplList<nullptr, 0> > == 1);
static_assert(metaFindV_v< 2, ValueTplList<1, (long)2, 2, 3, 4> > == 2);
static_assert(metaFindV_v< -1, ValueTplList<-2, -1, 42> > == 1);
int main ()
{
}
Комментарии:
1. вау, это действительно интересно. MSVC и gcc проглатывают это, даже с
assert< 2,... == 2);
(на позапрошлом). Но клэнг ничего этого слышать не хочет. В нем говорится,invalid operands to binary expression ('int' and 'nullptr_t')
что именно меня беспокоило по поводу ленивостиamp;amp;
оператора. Это не контекст SFINAE, поэтому вы не можете делать то, что вы делаете здесь 🙂 хотя некоторые компиляторы проглатывают это. вау2. Итак, у меня есть дополнительный вопрос о том, как это работает. вы приводите к
void
выражениюcomma
оператора? И эта часть:TargetVal == Values) || ... ||
что делает? Это повторение шаблона с многоточием в переменных, верно? какой шаблон он повторяет здесь? Рассмотренный шаблон останавливается передamp;amp;
? Я озадачен.3. @v.oddou — Я удивлен… Я был убежден, что с помощью
std::conjunction
вы можете решить проблему «недопустимых операндов», но это не так. В любом случае, я радикально решил проблему, добавивstrongSame
свойства пользовательского типа. Ответ изменен; теперь должен работать также с clang4. @v.oddou — о
void
приведении: заключается в том, чтобы избежать некоторых предупреждений (удалите его, и в clang вы должны получить еще 6 предупреждений); к сожалению, я не знаю, как удалить 3 оставшихся предупреждения: «множественные непоследовательные модификации ‘ind'»; Я не знаю, верно ли значение clang (и нет гарантированного порядкаind
выполнения), но кого это волнует, когда мы хотим просто увеличить счетчик?5. @v.oddou — о
TargetVal == Values) || ... ||
, да: это многоточие для вариационного повторения; новый синтаксис C 17 называется fold expression ; обратите внимание, что я использовал третью форму (pack op ... op init
); конечнаяinit
часть важна в случаях, когда совпадающее значение является последним вValues...
пакете
Ответ №2:
Я старался изо всех сил и потерпел неудачу. Похоже, это обычная ошибка в MSVC.
Моим решением для заинтересованных людей было полностью отказаться:'((( Вместо этого я использовал типы и оболочки значений, как показано ниже:
#include<type_traits>
#include<utility>
#include<variant>
template <typename... Types>
struct TypeList;
// auto: http://open-std.org/JTC1/SC22/WG21/docs/papers/2016/p0127r1.html
template <auto... Values>
struct ValueTplList;
// meta technique to find index of a type in a typelist
template <typename>
constexpr int MetaFind(int ind)
{ // not found case
return -1;
}
template <typename NeedleT, typename T, typename... Ts>
constexpr int MetaFind(int ind = 0)
{
if (std::is_same_v<NeedleT, T>)
{
return ind;
}
else
{
return MetaFind<NeedleT, Ts...>(ind 1);
}
}
// flat 2 template parameters versions
template <typename T, typename T2>
struct MetaIndexOf : std::integral_constant<int, -1>
{};
template <typename T>
struct MetaIndexOf<T,T> : std::integral_constant<int, 0>
{};
// destructurer version (access the contents of a typelist)
template <typename T, template<typename...> class Tmpl, typename... Ts>
struct MetaIndexOf<T, Tmpl<Ts...> >
: std::integral_constant< int, MetaFind<T, Ts...>() >
{};
// template variable version of it:
template <typename T, typename TemplInst>
constexpr int metaFind_v = MetaIndexOf<T, TemplInst>::value;
// test
static_assert(metaFind_v< bool, bool > == 0 );
static_assert(metaFind_v< bool, int > == -1 );
static_assert(metaFind_v< int, TypeList<bool, float, int, double> > == 2);
static_assert(metaFind_v< double, TypeList<bool, float, int, double> > == 3);
static_assert(metaFind_v< bool, TypeList<bool, float, int, double> > == 0);
static_assert(metaFind_v< long, TypeList<bool, float, int, double> > == -1);
// make a metaFind for values:
// let's try with a value wrapped in a type
template <auto V>
struct AnyConstantV : std::integral_constant<decltype(V), V>
{};
template <typename>
struct BoxAll;
// convenience helper to directly make a typelist of boxed values from values
template< template<auto...> class VL, auto... Vs >
struct BoxAll< VL<Vs...> >
{
using type = TypeList< AnyConstantV<Vs>... >;
};
static_assert( std::is_same_v< BoxAll<ValueTplList<0>>::type, TypeList<AnyConstantV<0>> > );
template <auto V, typename VL>
constexpr int metaFindV_v = metaFind_v< AnyConstantV<V>, typename BoxAll<VL>::type >;
// tests
static_assert(metaFindV_v< 0, ValueTplList<0> > == 0 );
static_assert(metaFindV_v< 5, ValueTplList<0> > == -1);
static_assert(metaFindV_v< 0, ValueTplList<nullptr, 0> > == 1);
// god dammnit visual studio ! ..... :(
static_assert(metaFindV_v< 2, ValueTplList<1, (long)2, 2, 3, 4> > == 2);
static_assert(metaFindV_v< -1, ValueTplList<-2, -1, 42> > == 1);
Вы можете поиграть с этим на:
https://godbolt.org/z/bnfZ5r
Обратите внимание, что static_assert
с 2 с треском проваливается и ошибочно. Опять же, clang здесь проходит нормально. И, что самое смешное, я не шучу, intellisense все понимает правильно: (MSVC создается без assert здесь, где это должно быть)