Is there a way to use the detection idiom (or another method) to test whether a function is valid for given template arguments, if it fails due to a static_assert
?
The example below illustrates that validity of foo
(failing return type computation) is detected as intended, but that of bar
(failing static_assert
) is not.
#include <iostream>
#include <type_traits>
template <typename... T> using void_t = void;
template <class AlwaysVoid, template<class...> class Op, class... Args>
struct detector: std::false_type { };
template <template<class...> class Op, class... Args>
struct detector<void_t<Op<Args...>>, Op, Args...>: std::true_type { };
template <template<class...> class Op, class... Args>
constexpr bool is_detected = detector<void, Op, Args...>::value;
template <typename T>
std::enable_if_t<!std::is_void<T>::value> foo() {
std::cout << "foo" << std::endl;
}
template <typename T>
void bar() {
static_assert( !std::is_void<T>::value );
std::cout << "bar" << std::endl;
}
template <typename T> using foo_t = decltype(foo<T>());
template <typename T> using bar_t = decltype(bar<T>());
int main(int argc, char* argv[]) {
foo<int>();
// foo<void>(); // fails as expected
bar<int>();
// bar<void>(); // fails as expected
std::cout << std::boolalpha;
// detection works for foo
std::cout << is_detected<foo_t,int > << std::endl; // true
std::cout << is_detected<foo_t,void> << std::endl; // false
// but not for bar
std::cout << is_detected<bar_t,int > << std::endl; // true
std::cout << is_detected<bar_t,void> << std::endl; // true !!!
}
This is the reason I can't detect if a boost::lexical_cast
is valid for given types.
It's not possible to use SFINAE to get the proper output here, because SFINAE rules operate on declarations, not definitions.
The type of bar
as declared will always be void(void)
, so the declaration is okay as far as SFINAE is concerned.
If you write up a real detection idiom (Like I did here), and use it like so:
template <typename T>
using CanCallFoo_t = decltype(&foo<T>);
template<class T>
using CanCallFoo = detect<T, CanCallFoo_t, void>;
template<class T>
using CanCallBar_t = decltype(&bar<T>);
template< class T>
using
CanCallBar = detect<T, CanCallBar_t, void>;
//...
std::cout << CanCallFoo<int>::value << std::endl; // true
std::cout << CanCallFoo<void>::value << std::endl; // false
std::cout << CanCallBar<int>::value << std::endl;
std::cout << CanCallBar<void>::value << std::endl;
You'll notice that SFINAE succeeds and then you get a compiler error when the definition is parsed.
error: static assertion failed
static_assert( !std::is_void<T>::value );
Demo
Notice that it works with foo
because foo
's declared type will fail SFINAE for void
The point of static_assert
is to make compilation fail if no other better match is found, not as a substitution for SFINAE.
Andy's answer is correct, in explaining that detecting a static_assert or any definition substitution failure is not possible with sfinae. I wanted to point out though, that it is possible to solve your problem, though you will need to bear the brunt of some repetition.
Basically, you need to find out what kinds of operations lexical_cast
is trying to apply to a generic type. Then you wrap lexical_cast
with your own function, and sfinae your function on whatever properties lexical_cast
requires. This isn't elegant but I doubt there are more than a few requirements on the type relevant to your application of interest, so it is a practical solution (perhaps).
Maybe this is obvious but I thought I would mention this since it's not yet covered.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With