使用 c++17,我们有花哨的新is_invocable
和花哨的新 prvalues 不是真正的值。
这允许您创建一个对象,而不必先在逻辑上构造它,然后省略构造。
我遇到了一个问题,使用 std::is_invocable
来测试是否可以调用某些东西,并且 prvalue 规则似乎发生了冲突:
struct no_move {
no_move(no_move&&)=delete;
explicit no_move(int) {}
};
void f( no_move ) {}
现在我们可以问一下是否可以使用 no_move
类型的 prvalue 调用f
?
f( no_move(1) )
std::is_invocable< decltype(&f), no_move >
不起作用,因为它使用std::declval<no_move>()
,这是一个像no_move&&
而不是 no_move
类型的 prvalue 。
在 c++14 中,这是相同的,但保证省略使某些函数可以使用 xvalue (即 "T&&
"( 调用,而其他函数可以使用 T
类型的 prvalues 调用。
有没有其他选择,或者我们必须发明自己的特质来处理这种情况?
(在一个理论世界中,std::declval<T>
返回T
而不是T&&
,我相信is_invocable
会做正确的事情(。
您滥用了 Invocable 概念。这个概念只意味着在给定函数和提供的参数上使用std::invoke
的能力。
你不能做std::invoke(f, no_move(1))
,因为这会引发转发参数的复制/移动。通过像 invoke
这样的转发调用将 prvalue 用作参数是不可能的。您可以将 prvalue 传递给转接调用,但对给定函数的最终调用将获得一个 xvalue。
这是避免在函数中使用 immobile 类型作为值参数的一个很好的理由。取而代之的是const&
。
C++没有类型特征来查看是否可以以您想要的方式使用特定参数调用函数。
有没有其他选择,或者我们必须发明自己的特质来处理这种情况?
是的,你只需要写你自己的不使用declval
的特征。假设你有std::is_detected
躺着(我知道你肯定有(:
template <typename T> T make();
template <typename F, typename... Args>
using invoke_result_t = decltype(std::declval<F>()(make<Args>()...));
// ^^^^^^^^^^^^^ ^^^^^
template <typename F, typename... Args>
using is_invocable = std::is_detected<invoke_result_t, F, Args...>;
这样,std::is_invocable<decltype(f), no_move>
false_type
,但is_invocable<decltype(f), no_move)>
true_type
。
我故意将declval<F>()
用于函数而不是make
,以便允许在此处使用decltype(f)
。真的,invoke_result_t
应该更复杂,并且"做正确的事"来指向成员等。但这至少是一个简单的近似值,表明这种方法的可行性。