我有一个仅将函数调用转发到另一个类的类,并且我希望能够在我的转发类上使用std::invocable<>
。但是由于某些原因而失败...这是我应该期待的吗?有没有解决的办法?
#include <type_traits>
#include <utility>
struct Foo {
constexpr int operator()( int i ) const {
return i;
}
};
struct ForwardToFoo {
template<class ...Args>
constexpr decltype(auto) operator()( Args &&...args ) const {
Foo foo;
return foo( std::forward<Args>( args )... );
}
};
int main( void ) {
// These work fine
static_assert( std::is_invocable_v<ForwardToFoo, int> == true );
static_assert( std::is_invocable_v<Foo, int> == true );
static_assert( std::is_invocable_v<Foo> == false );
// This causes a compiler error
static_assert( std::is_invocable_v<ForwardToFoo> == false );
return 0;
}
编辑:到目前为止的答案表明,问题在于最后一个static_assert()
会强制将ForwardToFoo::operator()<>
实例化为没有参数,从而触发编译器错误。那么,有没有一种方法可以将该实例化错误转换为SFINAE错误,而无需编译器错误就可以对其进行处理?
您遇到与您相同的错误
ForwardToFoo{}();
您具有operator()
中的ForwardToFoo
可以不带参数地调用。但是,当它在Foo()
中调用运算符时,如果没有参数,则会出现错误。
是否有解决方法?
是:只有当可使用参数调用ForwardToFoo()::operator()
时,才可以SFINAE启用Foo()::operator()
。
我的意思是……您可以按如下方式编写ForwardToFoo()::operator()
template<class ...Args>
constexpr auto operator()( Args &&...args ) const
-> decltype( std::declval<Foo>()(std::forward<Args>(args)...) )
{ return Foo{}( std::forward<Args>( args )... ); }
我无法完全弄清为什么您希望这样做有效。
Foo
要求int
是可调用的,因此ForwardToFoo
也是如此。否则,其对Foo
的调用将格式错误。
[您是否转发参数,复制参数或其他任何东西都没有关系:仍然需要提供它们。
考虑如何调用ForwardWithFoo
。你能不用争论吗?会发生什么?
问题与转发是否无关。在最后一个static_assert
中,您要求编译器将不带参数的ForwardToFoo::operator()
实例化。在该运算符中,您调用的Foo::operator()
仅具有一个int的重载。显然,这会导致硬编译器错误。
std::is_invocable
在其他情况下有效,因为它实际上不会实例化不存在的运算符。
您对代码的潜在解决方法是强制至少一个参数传递给ForwardToFoo::operator()
:
struct ForwardToFoo {
template<class Arg0, class ...Args>
constexpr decltype(auto) operator()( Arg0 arg0, Args ...args ) const {
Foo foo;
return foo(arg0, args...);
}
};
然后,编译器将不会实例化该运算符(因为没有参数就无法调用它。)>
或者您可以使用其他答案中所示的expression-sfinae。
请参见live example