让 SFINAE 在重载的函数对象上使用 `is_callable`
Getting SFINAE to work with `is_callable` on an overloaded function object
考虑以下函数对象l
:
auto l = [](auto x){ x.foo(); };
我可以成功地 static_assert
它可以被具有 .foo()
成员函数的类型调用:
struct Foo { void foo(); };
static_assert(std::is_callable<decltype(l)(Foo)>{});
如果我使用的类型没有 .foo()
成员函数,我现在希望 std::is_callable
求值为 std::false_type
:
static_assert(!std::is_callable<decltype(l)(int)>{});
不幸的是,上面的static_assert
导致编译错误:
prog.cc: In instantiation of '<lambda(auto:1)> [with auto:1 = int]':
prog.cc:8:44: required by substitution of 'template<class TF, class ... Ts>
struct is_callable<TF(Ts ...),
std::void_t<decltype (declval<TF>()((declval<Ts>)()...))>
> [with TF = <lambda(auto:1)>; Ts = {int}]'
prog.cc:17:50: required from here
prog.cc:12:24: error: request for member 'foo' in 'x', which is of non-class type 'int'
auto l = [](auto x){ x.foo(); };
~~^~~
我也尝试使用 std::void_t
实现我自己的 is_callable
,如下所示,得到相同的编译错误:
template <typename, typename = void>
struct is_callable : std::false_type { };
template <typename TF, class... Ts>
struct is_callable<TF(Ts...),
std::void_t<decltype(std::declval<TF>()(std::declval<Ts>()...))>>
: std::true_type { };
我的印象是,如果 std::void_t<decltype(/* ... */)>
中的表达式由于 SFINAE 而无效,则会选择 std::false_type
回退。
为什么这里SFINAE没有发生,导致编译错误?
我怎样才能实现我想要的行为? (即如果调用重载函数对象是错误的,则评估为 std::false_type
-形成)
- 请注意,在我的实际用例中,我将无法访问我正在执行
is_callable
检查的 函数对象 的实现。
您的 lambda 不限制它与 sfinae 的参数。您的 lambda 可以用任何东西调用,但在使用这些参数实例化时会触发硬编译错误。
要获得预期效果,请对 return 类型施加约束:
auto l = [](auto x) -> void_t<decltype(x.foo())> { x.foo(); };
这样,is_callable
特性将产生正确的结果。
Why is this SFINAE not taking place here, resulting in a compilation error?
因为 l
可以用任何东西调用 - 编译错误发生在它的主体被实例化时,它发生在 "isolated environment" SFINAE 正在使用的地方之外地点。
约束 lambda 将解决问题:
auto l = [](auto x) -> void_t<decltype(x.foo())> { x.foo(); };
(请注意,此处使用 void_t
,因为 lambda 未返回 x.foo()
。)
How can I achieve my desired behavior?
(i.e. evaluate to std::false_type
if calling the overloaded function object is ill-formed)
如果函数对象的签名没有得到适当的约束,将无法实现您想要的行为。
要做到这一点,需要编译器实现某种 "speculative compilation",由于实现难度大,这是不可取的。
本文中的更多信息:"Diagnosable validity"。
There is no way to check it from within C++, and there is not going to be any: it has been clearly laid out, that compiler vendors cannot be forced to implement a “speculative compilation” and backtracking from arbitrarily deep template instantiation failures.
考虑以下函数对象l
:
auto l = [](auto x){ x.foo(); };
我可以成功地 static_assert
它可以被具有 .foo()
成员函数的类型调用:
struct Foo { void foo(); };
static_assert(std::is_callable<decltype(l)(Foo)>{});
如果我使用的类型没有 .foo()
成员函数,我现在希望 std::is_callable
求值为 std::false_type
:
static_assert(!std::is_callable<decltype(l)(int)>{});
不幸的是,上面的static_assert
导致编译错误:
prog.cc: In instantiation of '<lambda(auto:1)> [with auto:1 = int]':
prog.cc:8:44: required by substitution of 'template<class TF, class ... Ts>
struct is_callable<TF(Ts ...),
std::void_t<decltype (declval<TF>()((declval<Ts>)()...))>
> [with TF = <lambda(auto:1)>; Ts = {int}]'
prog.cc:17:50: required from here
prog.cc:12:24: error: request for member 'foo' in 'x', which is of non-class type 'int'
auto l = [](auto x){ x.foo(); };
~~^~~
我也尝试使用 std::void_t
实现我自己的 is_callable
,如下所示,得到相同的编译错误:
template <typename, typename = void>
struct is_callable : std::false_type { };
template <typename TF, class... Ts>
struct is_callable<TF(Ts...),
std::void_t<decltype(std::declval<TF>()(std::declval<Ts>()...))>>
: std::true_type { };
我的印象是,如果 std::void_t<decltype(/* ... */)>
中的表达式由于 SFINAE 而无效,则会选择 std::false_type
回退。
为什么这里SFINAE没有发生,导致编译错误?
我怎样才能实现我想要的行为? (即如果调用重载函数对象是错误的,则评估为
std::false_type
-形成)- 请注意,在我的实际用例中,我将无法访问我正在执行
is_callable
检查的 函数对象 的实现。
- 请注意,在我的实际用例中,我将无法访问我正在执行
您的 lambda 不限制它与 sfinae 的参数。您的 lambda 可以用任何东西调用,但在使用这些参数实例化时会触发硬编译错误。
要获得预期效果,请对 return 类型施加约束:
auto l = [](auto x) -> void_t<decltype(x.foo())> { x.foo(); };
这样,is_callable
特性将产生正确的结果。
Why is this SFINAE not taking place here, resulting in a compilation error?
因为 l
可以用任何东西调用 - 编译错误发生在它的主体被实例化时,它发生在 "isolated environment" SFINAE 正在使用的地方之外地点。
约束 lambda
auto l = [](auto x) -> void_t<decltype(x.foo())> { x.foo(); };
(请注意,此处使用 void_t
,因为 lambda 未返回 x.foo()
。)
How can I achieve my desired behavior? (i.e. evaluate to
std::false_type
if calling the overloaded function object is ill-formed)
如果函数对象的签名没有得到适当的约束,将无法实现您想要的行为。
要做到这一点,需要编译器实现某种 "speculative compilation",由于实现难度大,这是不可取的。
本文中的更多信息:"Diagnosable validity"。
There is no way to check it from within C++, and there is not going to be any: it has been clearly laid out, that compiler vendors cannot be forced to implement a “speculative compilation” and backtracking from arbitrarily deep template instantiation failures.