w C++ 03 po prostu napisać enable_if
siebie. Nie wymaga funkcji C++ 11.
Powodem, dla którego używasz różnych technik jest to, że kompilatory sprzed C++ 11 czasami mają zabawną definicję SFINAE i co powinno być błędem. MSVC jest obecnie głównym kompilatorem, który wciąż (w erze przed C++ 17) ma bardzo dziwną definicję tego, co jest poprawne SFINAE ze względu na problemy z "SFINAE decltype".
w C++ 11 należy wpisać void_t
i enable_if_t
uprościć swoje rzeczy SFINAE.
Należy również napisać to:
namespace details {
template<template<class...>class Z, class always_void, class...Ts>
struct can_apply:std::false_type{};
template<template<class...>class Z, class...Ts>
struct can_apply<Z, void_t<Z<Ts...>>, Ts...>:std::true_type{};
}
template<template<class...>class Z, class...Ts>
using can_apply = details::can_apply<Z, void, Ts...>;
który pozwala pisać cechy i zapytać, czy coś jest ważne łatwo (można wywołać metodę Tworzenie aliasu że robi decltype
na pw, a następnie zapytać, czy możesz zastosować typ do aliasu). Jest to nadal potrzebne w C++ 14 i 17, ale C++ 20 prawdopodobnie dostanie is_detected
, który służy podobnemu celowi.
Więc can_print
jest:
template<class T>using print_result = decltype(
std::declval<std::ostream&>() << std::declval<T>()
);
template<class T>using can_print = can_apply< print_result, T >;
to albo truthy lub falsy zależności czy <<
prac nad strumieniem z nim.
W C++ 14 możesz zacząć korzystać z metaprogramowania w stylu hana, aby utworzyć lambdy, które wprowadzają manipulację. W C++ 17 stają się constexpr
, co pozbywa się niektórych problemów.
Stosowanie takich technik, jak makro OP, prowadzi zwykle do źle sformułowanych programów, nie wymaga diagnostyki. Dzieje się tak dlatego, że jeśli szablon nie ma poprawnych parametrów szablonu, które prowadziłyby do tego, że treść szablonu jest poprawnym kodem, twój program jest źle sformułowany, nie wymaga diagnostyki.
Więc tak:
template<REQUIRES(sizeof(int)==4)>
int fun() {
// code that is ill-formed if `int` does not have size 4
}
najprawdopodobniej skompilować i uruchomić i „rób co chcesz”, ale to jest rzeczywiście źle sformułowane podczas sizeof(int)
Program jest 8
.
To samo może się zdarzyć w przypadku użycia tej techniki do wyłączenia metod na klasach na podstawie argumentów szablonu klasy. Stanard jest niejasny w tej kwestii, więc go unikam.
Makro REQUIRES
próbuje ukryć, jak działa za magią, ale zbyt łatwo jest przekroczyć linię i wygenerować źle sformułowany program. Ukrywanie magii, gdy szczegóły magii powodują, że twój kod jest źle sformułowany, nie jest dobrym planem.
Wysyłanie tagów może być wykorzystane do uproszczenia skomplikowanych problemów SFINAE. Może być używany do porządkowania przeciążeń lub wybierania między nimi lub przekazywania więcej niż jednego pakietu typów do funkcji szablonu pomocy.
template<std::size_t N>
struct overload_priority : overload_priority<N-1> {};
template<>
struct overload_priority<0> {};
Teraz można przejść overload_priority<50>{}
do zestawu funkcji, a jedną z najwyższaoverload_priority<?>
w tym gnieździe będą korzystne.
template<class T>struct tag_t{using type=T;};
namespace details {
inline int fun(tag_t<int[4]>) { return 0; }
inline int fun(tag_t<int[8]>) { return 1; }
}
int fun() { return details::fun(tag_t<int[sizeof(int)]>{}); }
wysłane do innej funkcji w zależności od rozmiaru int
.
Obie przeciążenia fun
są kompilowane i sprawdzane, więc nie napotkasz na problem z ukrycia źle sformułowanego programu.
funkcją, której ważność jest nie funkcją jej argumentów szablonu jest nie bezpiecznie używać w C++. Musisz użyć innej taktyki. Maszyny, które ułatwiają to zadanie, ułatwiają pisanie źle sformułowanych programów.
To wygląda świetnie! – vsoftco
Szczerze mówiąc, dlaczego nie wystarczy wpisać nazwę std :: enable_if = 0'? Ach, nie zależy to od parametru szablonu. Dlaczego "require_enum"? Podpis kolizji? –
Yakk
@Zobacz link do wpisu dodanego pod przykładem. – xinaiz