Продолжая баловаться со static_reflection захотел уметь проверять возможно ли проитерироваться по заданному типу используя C++11 range-for. Попытавшись написать вот так:
template<typename T, typename = void_t<>> struct is_iterable: false_type {}; template<typename T> struct is_iterable< T, void_t< decltype(for (const auto& i: declval<const T&>()) {}) > >: true_type {};я разумеется сразу же понял, что Expression SFINAE неприменим для проверки правильности того, что является Statement с точки зрения языка C++. Опечалившись этим фактом я решил воспользоваться наличием под рукой GCC 6.1 известного тем, что это единственный в мире компилятор с поддержкой концептов. И так родился следующий код:
template<typename T> concept bool Iterable = requires(const T& t) { {for (const auto& item: t) {}}; };который был безбожно забрит компилятором. Чтение спецификации Concepts Light TS подтвердило правоту компилятора, но пошатнуло мою веру в разумное, доброе, вечное. Оказывается requires позволяет проверять возможность использования своих операндов в выражениях (expressions), а в операциях (statement) не позволяет.
Всё ещё опечаленный сим фактом я вернулся к имеющемуся у меня в боевых условиях GCC 4.9 написал работающую проверку использую многа ненужных букаф:
using std::begin; using std::end; template<typename T, typename = void_t<>> struct is_iterable: false_type {}; template<typename T> struct is_iterable< T, void_t< decltype(begin(declval<const T&>())), decltype(end(declval<const T&>())), decltype(begin(declval<const T&>()) != end(declval<const T&>())), decltype(++begin(declval<const T&>())), decltype(*begin(declval<const T&>())) > >: true_type {};И пошёл компилировать всё это дело в стуиях. 15 это всё съела, чем меня несказанно порадовала. Это впервые на моей памяти, когда компилятор от мелкомягких просто и спокойно скомпилировал нетривиальный шаблон отлаженный на GCC с CLANG'ом.
Но из за WinPhone8 я был обречён на подлый удар в спину от студии 13ой. Гугл, stack-overflow и два дня экспериментов позволили найти выход. Который уже работает везде:
using std::begin; using std::end; template<typename T> auto test_begin(const T& t) -> decltype(begin(t)); auto test_begin(...) -> false_type; template<typename T> auto test_end(const T& t) -> decltype(end(t)); auto test_end(...) -> false_type; template<typename T> auto test_it_noteq_end(const T& t) -> decltype(begin(t) != end(t)); auto test_it_noteq_end(...) -> false_type; template<typename T> auto test_it_increment(const T& t) -> decltype(++begin(t)); auto test_it_increment(...) -> false_type; template<typename T> auto test_it_deref(const T& t) -> decltype(*begin(t)); auto test_it_deref(...) -> false_type; template<typename T> using is_iterable = typename conditional< ( !is_same<decltype(test_begin(declval<const T&>())), false_type>::value && !is_same<decltype(test_end(declval<const T&>())), false_type>::value && !is_same<decltype(test_it_noteq_end(declval<const T&>())), false_type>::value && !is_same<decltype(test_it_increment(declval<const T&>())), false_type>::value && !is_same<decltype(test_it_deref(declval<const T&>())), false_type>::value ), true_type, false_type >::type;
И всё же я безумно хочу, чтобы можно было написать вот так:
template<typename T> concept bool Iterable = requires(const T& t) { {for (const auto& item: t) {}}; };ибо понять такой код неправильно много сложней чем понять правильно финальную версию рабочей реализации моего трейта.
Комментариев нет:
Отправить комментарий