Let's say we are given an instance of the template Container<U, Args...> (I think Container is std::vector ) and an asymmetric type T, and we need to check if we can call push_back on the object, enter Container<T> . Here is the code that uses the idiom of the detector:
#include <iostream> #include <vector> #include <set> #include <string> #include <type_traits> #include <boost/iterator.hpp> #include <boost/range.hpp> template<typename, typename> struct replace { using type = struct Error; }; template<template<typename...> class Container, typename U, typename T> struct replace<Container<U>, T> { using type = Container<T>; }; template<typename Container, typename T> using replace_t = typename replace<Container, T>::type; template<typename Placeholder, template<typename...> class Op, typename... Args> struct isDetected : std::false_type {}; template<template<typename...> class Op, typename... Args> struct isDetected<std::void_t<Op<Args...>>, Op, Args...> : std::true_type {}; template<typename Container, typename T> using pushBackDetector = decltype(std::declval<Container&>().push_back(std::declval<T>())); template<typename Container, typename T> bool canPushBack() { return isDetected<void, pushBackDetector, Container, T> {}; } int main() { std::cout << canPushBack<replace_t<std::vector<int>, double>, double>() << std::endl; std::cout << canPushBack<replace_t<std::set<int>, double>, double>() << std::endl; std::cout << canPushBack<replace_t<boost::iterator_range<std::string::iterator>, std::string::iterator>, double>() << std::endl; //std::cout << canPushBack<replace_t<boost::iterator_range<std::string::iterator>, int>, double>() << std::endl; }
A tough example is available on Wandbox.
Indeed, it correctly infers that we can call push_back on std::vector<double> , but we cannot do this on std::set<double> or boost::iterator_range<std::string::iterator> .
Now let me check if we can call push_back on boost::iterator_range<int> and uncomment the last line! And now the code explodes so beautifully that I wonโt give a complete error message here (itโs better to do this with the examples above), but the essence of this is that the compiler tries to create an instance of boost::iterator_range<int> and turn the failure into enter some basic type of this into a hard error:
/opt/wandbox/boost-1.65.1/clang-5.0.0/include/boost/iterator/iterator_categories.hpp:119:60: error: no type named 'iterator_category' in 'std::__1::iterator_traits<int>' typename boost::detail::iterator_traits<Iterator>::iterator_category ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~ /opt/wandbox/boost-1.65.1/clang-5.0.0/include/boost/range/iterator_range_core.hpp:156:32: note: in instantiation of template class 'boost::iterators::iterator_traversal<int>' requested here BOOST_DEDUCED_TYPENAME iterator_traversal<IteratorT>::type ^ /opt/wandbox/boost-1.65.1/clang-5.0.0/include/boost/range/iterator_range_core.hpp:436:67: note: in instantiation of template class 'boost::iterator_range_detail::pure_iterator_traversal<int>' requested here BOOST_DEDUCED_TYPENAME iterator_range_detail::pure_iterator_traversal<IteratorT>::type ^ prog.cc:31:61: note: in instantiation of template class 'boost::iterator_range<int>' requested here using pushBackDetector = decltype(std::declval<Container&>().push_back(std::declval<T>())); ^ prog.cc:28:31: note: in instantiation of template type alias 'pushBackDetector' requested here struct isDetected<std::void_t<Op<Args...>>, Op, Args...> : std::true_type {}; ^ prog.cc:36:12: note: during template argument deduction for class template partial specialization 'isDetected<std::void_t<Op<Args...> >, Op, Args...>' [with Op = pushBackDetector, Args = <boost::iterator_range<int>, double>] return isDetected<void, pushBackDetector, Container, T> {}; ^
On the one hand, it makes sense - indeed, int not an iterator. On the other hand, it is highly desirable to catch this incorrect creation and simply return false from canPushBack() in this case. So, here is the question: is it possible to turn this hard error into a soft error and handle it gracefully?