One way is to define the predicate constexpr, which checks the type of its argument, and then constexpr to include the result of this predicate.
I think this method is good because it separates functional logic from precondition logic.
#include <iostream> #include <cstddef> #include <type_traits> class MyTest { public: template<typename T> void do_something(T value) { // define our predicate // lambdas are constexpr-if-possible in c++17 constexpr auto is_supported = [](auto&& x) { if constexpr (std::is_same<std::decay_t<decltype(x)>, std::uint64_t>()) return true; else return false; }; // use the result of the predicate if constexpr (is_supported(value)) { std::cout << "supported\n"; } else { std::cout << "not supported\n"; } } }; int main() { auto t = MyTest(); t.do_something(int(0)); t.do_something(std::uint64_t(0)); t.do_something(double(0)); t.do_something(static_cast<unsigned long>(0)); // be careful with std::uint_xx aliases }
Examples of results:
not supported supported not supported supported
Another way to express this could be:
class MyTest { public: template<class T> static constexpr bool something_possible(T&&) { return std::is_same<std::decay_t<T>, std::uint64_t>(); } template<typename T> void do_something(T value) {
source share