const einfach
-
Haltet ihr so etwas für nützlich ?
template <typename T> constexpr typename std:conditional<std::is_reference<T>::value, const typename std::remove_reference<T>::type&, const T&&>::type auto_const(T&& x) { return std::forward<T>( x ); }Anwendung z.B. in
vector <int> v = { ... }; for ( auto& x : v ) // vector<int>::iterator ; for ( const auto& x : v ) // vector<int>::iterator, x ist const lvalue ; for ( auto& x : auto_const( v ) ) // vector<int>::const_iterator ; // const Überladung struct foo { const int& operator[](int x) const; int& operator[](int x) { return const_cast<int&>(auto_const(*this)[x]); } };Die Idee ist, den sonst nötigen expliziten aber völlig unkritischen Cast zu vermeiden, in dem auch noch mal explizit der Typ aufgeschrieben werden muss.
-
Könntest du vielleicht ein wenig mehr dazu schreiben?
Was der Code macht und was das Ziel ist?Mir persönlich erschließt sich nicht sofort was das Ziel ist, aber ich bin vielleicht auch die falsche Zielgruppe.
-
for ( const auto& x : v ) // vector<int>::iterator, x ist const lvalue ; for ( auto& x : auto_const( v ) ) // vector<int>::const_iterator ;Auch wenn das zweite vielleicht theoretisch etwas sauberer ist, ich kann mir nicht vorstellen, dass ein
const_iteratorschneller ist als ein normaleriterator. Vom Code her gefällt mir die erste Variante besser weil sofort klar ist, dass x const ist und das v nicht verdeckt wird.Praktisch fände ich das hingegen bei Parameterübergaben.
std::find(v, 'x'); // wird v hier verändert? std::find(auto_const(v), 'x'); // hier nicht // (falls mal endlich Ranges kommen)
-
Ist nützlich. Zeiger sind aber stets spaßiger. Erst brauchen wir
apply_cv:template <typename...> struct type_list; // Das kommt einfach mal hierhin #include <type_traits> #define DEFINE_ALIAS_T(name) \ template <typename T, typename U> \ using name##_t = typename name<T, U>::type; /// -------------------------------------------------------------------------------------------------------------- template <typename T, typename U> struct apply_const : std::conditional<std::is_const <T>::value, U const , U> {}; template <typename T, typename U> struct apply_volatile : std::conditional<std::is_volatile<T>::value, U volatile, U> {}; DEFINE_ALIAS_T(apply_const) DEFINE_ALIAS_T(apply_volatile) template <typename T, typename U> struct apply_cv : apply_const<T, apply_volatile_t<T, U>> {}; DEFINE_ALIAS_T(apply_cv) /// -------------------------------------------------------------------------------------------------------------- template <typename T, typename U> struct subtract_const : std::conditional<std::is_const <T>::value, std::remove_const_t <U>, U> {}; template <typename T, typename U> struct subtract_volatile : std::conditional<std::is_volatile<T>::value, std::remove_volatile_t<U>, U> {}; DEFINE_ALIAS_T(subtract_const) DEFINE_ALIAS_T(subtract_volatile) template <typename T, typename U> struct subtract_cv : subtract_const<T, subtract_volatile_t<T, U>> {}; DEFINE_ALIAS_T(subtract_cv)Referenzen können nicht cv-qualifiziert sein, Zeiger schon. Und dann müssen die Veränderungen ja auf vorgebbaren Ebenen stattfinden.
template <typename, typename ...> struct ptr_unwrap; template <typename Res, typename first, typename... tail> struct ptr_unwrap<Res, first, tail...> : ptr_unwrap<apply_cv_t<first, Res*>, tail...> {}; template <typename Res> struct ptr_unwrap<Res> {using type = Res;}; /// -------------------------------------------------------------------------------------------------------------- template <template<typename, typename> class, typename, typename, unsigned, typename = type_list<>, typename = void> struct modify_ptr_qualifier_impl; template <template<typename, typename> class Modifier, typename Mask, typename T, unsigned counter, typename... types> struct modify_ptr_qualifier_impl<Modifier, Mask, T, counter, type_list<types...>, std::enable_if_t<std::is_pointer<T>::value && counter != 0>> : modify_ptr_qualifier_impl<Modifier, Mask, std::remove_pointer_t<T>, counter-1, type_list<T, types...>> {}; template <template<typename, typename> class Modifier, typename Mask, typename T, unsigned counter, typename... types> struct modify_ptr_qualifier_impl<Modifier, Mask, T, counter, type_list<types...>, std::enable_if_t<!std::is_pointer<T>::value || counter == 0>> : ptr_unwrap<typename Modifier<Mask, T>::type, types...> {};Maskenthält dabei die zu verändernden qualifier.Modifierführt die Modifizierungen durch,counterzählt herunter.Anschließend noch die Namen definieren:
#include <limits> #define DEFINE_APPLY_PQ(apply_name, templ, name, cv) \ template <typename T, unsigned counter = std::numeric_limits<unsigned>::max()> \ struct apply_name##_ptr_##name : modify_ptr_qualifier_impl<templ##_##name, void cv, T, counter> {}; \ \ template <typename T, unsigned counter = std::numeric_limits<unsigned>::max()> \ using apply_name##_ptr_##name##_t = typename apply_name##_ptr_##name<T, counter>::type; DEFINE_APPLY_PQ(remove, subtract, const, const) DEFINE_APPLY_PQ(remove, subtract, volatile, volatile) DEFINE_APPLY_PQ(remove, subtract, cv, const volatile) DEFINE_APPLY_PQ(add, apply, const, const) DEFINE_APPLY_PQ(add, apply, volatile, volatile) DEFINE_APPLY_PQ(add, apply, cv, const volatile)Und ein klitzekleines Testprogramm:
template <typename U, typename V> constexpr void assert_same() { static_assert (std::is_same<U, V>::value, ""); } int main() { assert_same<add_ptr_const_t<int volatile* volatile* const volatile, 1>, int volatile* const volatile* const volatile>(); assert_same<add_ptr_const_t<int volatile* volatile* const volatile, 2>, int const volatile* volatile* const volatile>(); assert_same<add_ptr_cv_t <int**, 1>, int* const volatile*>(); assert_same<remove_ptr_const_t<int volatile* const* const volatile, 1>, int volatile** const volatile>(); assert_same<remove_ptr_volatile_t<int volatile* volatile* const volatile, 2>, int* volatile* const volatile>(); assert_same<remove_ptr_cv_t<int* const volatile*, 1>, int**>(); }
-
Arcoth schrieb:
Zeiger sind aber stets spaßiger.
Und wo würde ich das einsetzen?
template <typename T, typename U> struct apply_const : std::conditional<std::is_const <T>::value, U const , U> {};das conditional kannst du dir auch sparen. Bringt mich aber auf eine spassige Implementation von identity:
template <typename T> struct identity : std::conditional<true, T, void> {};
-
Und wo würde ich das einsetzen?
Habe ich etwa versehentlich den Eindruck erweckt dass das irgendwo praktisch einsetzbar wäre? War nur zum Spaß.

das conditional kannst du dir auch sparen
Ja, indem ich selber partiell Spezialisiere. Das bedeutet nur mehr Schreibaufwand; Warum sollte ich auf die Type-Traits verzichten wollen?
Oder hast du etwa einen cleveren Weg gefunden es zu schreiben (genauso lang aber ohne einconditional-Artiges Template)?Bringt mich aber auf eine spassige Implementation von identity:
Die habe ich doch schonmal gesehen! Wo bloß, vielleicht auf SO...?

-
Arcoth schrieb:
das conditional kannst du dir auch sparen
Ja, indem ich selber partiell Spezialisiere. Das bedeutet nur mehr Schreibaufwand; Warum sollte ich auf die Type-Traits verzichten wollen?
Oder hast du etwa einen cleveren Weg gefunden es zu schreiben (genauso lang aber ohne einconditional-Artiges Template)?ach ne, habe nicht richtig hingeschaut und missverstanden, was der Code tun soll..