diff options
-rw-r--r-- | src/development/c++.md | 12 | ||||
-rw-r--r-- | src/development/c++/Makefile | 2 | ||||
-rw-r--r-- | src/development/c++/tmpl-void_t.cc | 47 |
3 files changed, 52 insertions, 9 deletions
diff --git a/src/development/c++.md b/src/development/c++.md index 9dc8fb0..6cdc251 100644 --- a/src/development/c++.md +++ b/src/development/c++.md @@ -58,17 +58,13 @@ of the `decltype(std:declval<T>...` expressions is ill-formed, the template specialization for `is_valid` will be removed from the candidate set due to [SFINAE][sfinae]. ```cpp -template<typename T, typename = void> -struct is_valid : std::false_type {}; - -template<typename T> -struct is_valid<T, std::void_t< - decltype(std::declval<T>().some_fun1()), - decltype(std::declval<T>().some_fun2()) - >> : std::true_type {}; +{{#include c++/tmpl-void_t.cc:3:45}} ``` > `std::declval<T>()` creates an instance of type T in an unevaluated context. +A more detailed description is available in the SO discussion [How does +`void_t` work](https://stackoverflow.com/a/27688405). + ## Template selection with partially / fully specializations. ```cpp {{#include c++/tmpl-pair.cc:3:}} diff --git a/src/development/c++/Makefile b/src/development/c++/Makefile index 1d5172b..ca752d9 100644 --- a/src/development/c++/Makefile +++ b/src/development/c++/Makefile @@ -1,4 +1,4 @@ -SRC = concepts-11.cc meta.cc meta2.cc meta4.cc tmpl-pair.cc +SRC = concepts-11.cc meta.cc meta2.cc meta4.cc tmpl-pair.cc tmpl-void_t.cc BIN = $(SRC:.cc=) all: $(BIN) diff --git a/src/development/c++/tmpl-void_t.cc b/src/development/c++/tmpl-void_t.cc new file mode 100644 index 0000000..fd95315 --- /dev/null +++ b/src/development/c++/tmpl-void_t.cc @@ -0,0 +1,47 @@ +// Copyright (C) 2023 johannst + +#include <type_traits> + +// (1) Primary template. +template<typename T, typename = void> +struct is_valid : std::false_type {}; + +// (2) Partial template specialization. +template<typename T> +struct is_valid<T, std::void_t<decltype(std::declval<T>().some_fun1()), + decltype(std::declval<T>().some_fun2())>> : std::true_type {}; +struct A { + void some_fun1() {} + void some_fun2() {} +}; + +struct B {}; + +static_assert(is_valid<A>::value, "is true"); +// * Compare template arg list with primary template, we only supplied one +// arg, the second one will be defaulted as +// is_valid<A, void> +// * Compare template arg list against available specializations, this will +// try to match the pattern <A, void> against the patterns defined in the +// partial specializations. +// * Try specialization (2) +// * T -> A +// * Evaluate std::void_t -> decltype's are well-formed +// std::void_t<...> -> void +// * Specialization (2) matches <A, void> +// * Pick the most specialized version -> (2) + +static_assert(!is_valid<B>::value, "is false"); +// * Compare template arg list with primary template, we only supplied one +// arg, the second one will be defaulted as +// is_valid<A, void> +// * Compare template arg list against available specializations, this will +// try to match the pattern <B, void> against the patterns defined in the +// partial specializations. +// * Try specialization (2) +// * T -> B +// * Evaluate std::void_t -> decltype's are ill-formed +// * Specialization (2) is removed from candidate set, no hard error (SFINAE) +// * No specialization matches, take the primary template. + +int main() {} |