Let's say we're given a template instantiation Container<U, Args...> (think Container being an std::vector) and a non-template type T, and we need to check if we can call push_back on an object of type Container<T>. Here's the code that uses the detector idiom:
#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 live example is available at Wandbox.
Indeed, it correctly deduces that we can call push_back on std::vector<double>, but we can't do that on std::set<double> or on boost::iterator_range<std::string::iterator>.
Now let's 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 the full error message here (better to do that on the live example linked above), but the gist of it is the compiler trying to instantiate boost::iterator_range<int> and turning the failure to instantiate some base type of that one 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 one hand, this makes perfect sense — indeed, int is not an iterator. On the other hand, it's very desirable to catch this incorrect instantiation and just return false from canPushBack() in this case. So, this is the question: is it possible to turn this hard error into a soft error and handle it gracefully?