I have a problem with detecting when an instantiation of a generic lambda is well formed but not compilable, and detecting it has stumped me:
#include <functional>
class future
{
public:
int get() & { return 5; }
};
// Gets the return type of F(A), returning a not_well_formed type if not well formed
template<class F, class A> struct get_return_type
{
struct not_well_formed {};
template<class _F, class _A> static not_well_formed test(...);
template<class _F, class _A> static auto test(_F &&f) noexcept(noexcept(f(std::declval<_A>()))) -> decltype(f(std::declval<_A>()));
using type = decltype(test<F, A>(std::declval<F>()));
static constexpr bool is_noexcept = noexcept(test<F, A>(std::declval<F>()));
};
int main(void)
{
auto foo=[](auto &&x) { return x.get(); };
using type=get_return_type<decltype(foo), const future>::type;
return 0;
}
This fails with (on clang 3.7):
ned@kate:~$ clang++-3.7 -std=c++14 -o weird_generic_lambda_thing weird_generic_lambda_thing.cpp
weird_generic_lambda_thing.cpp:21:34: error: member function 'get' not viable: 'this' argument has type 'const future', but
function is not marked const
auto foo=[](auto &&x) { return x.get(); };
^
weird_generic_lambda_thing.cpp:14:111: note: in instantiation of function template specialization 'main()::(anonymous
class)::operator()<const future>' requested here
..._F, class _A> static auto test(_F &&f) noexcept(noexcept(f(std::declval<_A>()))) -> decltype(f(std::declval<_A>()));
^
weird_generic_lambda_thing.cpp:15:25: note: while substituting explicitly-specified template arguments into function
template 'test'
using type = decltype(test<F, A>(std::declval<F>()));
^
weird_generic_lambda_thing.cpp:22:14: note: in instantiation of template class 'get_return_type<(lambda at
weird_generic_lambda_thing.cpp:21:12), const future>' requested here
using type=get_return_type<decltype(foo), const future>::type;
^
weird_generic_lambda_thing.cpp:6:7: note: 'get' declared here
int get() & { return 5; }
^
1 error generated.
You can probably blame my inexperience with Expression SFINAE here (thanks Visual Studio!), but I am surprised: surely the decltype creating the return type of test() should fail to substitute if f(std::declval<_A>())
is not well formed?
Obviously the answer is that is does fail to substitute, but in a non-SFINAE way. Can the above be fixed so it correctly returns a not_well_formed type if the generic lambda is uncompilable with some arbitrary parameter type?
You cannot in general. Only early failures can be detected via SFINAE. Early failures are basically the declaration, not the definition, of a function (or class) template.
The lambda can provide SFINAE early failure instrumentation via explicitly declaring the return type ->decltype(x.get())
, or through other SFINAE techniques like enable_if_t
or void_t
.
The idea is that compilers are not required to fully compile functions in order to engage in overload resolution.
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