佳木斯建设局网站,中国最大网站建设商推荐知乎,如何引导企业老板做网站,网页视频怎么下载到本地视频手机一、函数调用方式
在学习编程的开始#xff0c;就接触到了函数和函数的调用。可以这样讲#xff0c;不会调用函数#xff0c;那么最基础的语言功能便无法使用了。无论是低级到汇编或者高级到哪个语言#xff0c;函数仍然是其重要的基础一环。可能随便一个学习过编程的都可…一、函数调用方式
在学习编程的开始就接触到了函数和函数的调用。可以这样讲不会调用函数那么最基础的语言功能便无法使用了。无论是低级到汇编或者高级到哪个语言函数仍然是其重要的基础一环。可能随便一个学习过编程的都可会说“调用函数不是特别简单么直接使用其函数名称就可以了再填上相关的参数”。更或者高级一些的使用类成员函数加个类限定符就可以了。 这都是正确的。但是如果在某些特殊的情况下想实现对函数的调用该怎么办这个特殊是指哪些 假如有一个场景需要依赖注入并在注入后执行相关的函数。那么注入的可能是什么一个普通的函数指针一个类成员函数指针一个lambda表达式一个std::function或std::mem_fn等等这都有可能。特别是在模板编程中这种情况更是无法控制除非使用SFINAE或者Conecpts进行控制。但如果实际的场景就是需要支持上述任意的类型呢 有过C#开发经验的知道 它有一个invoke方法可以调用相关的情况如委托等 。前面反复提及过高级语言的大趋势是趋向一致的所以C也从C17标准起提供了std::invoke(C17,c20)和std::invoke_rc23两个函数模板。
二、std::invoke和std::invoke_r
先看一下定义
std::invoke, std::invoke_rC Utilities library Function objects
Defined in header functional
template class F, class... Args
std::invoke_result_tF, Args...invoke( F f, Args... args ) noexcept(/* see below */); (1) (since C17)(constexpr since C20)
template class R, class F, class... Args
constexpr Rinvoke_r( F f, Args... args ) noexcept(/* see below */); (2) (since C23)1) Invoke the Callable object f with the parameters args as by INVOKE(std::forwardF(f), std::forwardArgs(args)...). This overload participates in overload resolution only if std::is_invocable_vF, Args... is true.
2) Invoke the Callable object f with the parameters args as by INVOKER(std::forwardF(f), std::forwardArgs(args)...). This overload participates in overload resolution only if std::is_invocable_r_vR, F, Args... is true.
Parameters
f - Callable object to be invoked
args - arguments to pass to f
Return value
1) The value returned by f.
2) The value returned by f, implicitly converted to R, if R is not (possibly cv-qualified) void. None otherwise.注意上面的constexpr。 其实很简单就是调用一个F定义的函数对象参数由Args…来给出。返回值是std::invoke_result_tF, Args…。它的定义有点吓人但使用起来却比较方便看下面的例子
#include functional
#include iostream
#include memoryvoid ge_func(int num) { std::cout call general function!value is: num std::endl; }
struct ExFunc {void operator()(int num) { std::cout call functor!value is: num std::endl; }
};
struct ExObjFunc {ExObjFunc() {}void ObjFunc(int num) { std::cout call object function!value is: num std::endl; }
};template typename F, typename T void TestInvoke(F f, T t) { std::invoke(f, std::forwardT(t)); }int main() {TestInvoke(ge_func, 1);TestInvoke(ExFunc(), 2);ExObjFunc eof;std::invoke(ExObjFunc::ObjFunc, eof, 3);std::functionvoid(int) func std::bind(ExObjFunc::ObjFunc, eof, std::placeholders::_1);TestInvoke(func, 3);TestInvoke([](int num) { std::cout call lambda!value is: num std::endl; }, 4);
}注意如果没有最新的环境建议直接只使用std::invoke. 上面的代码非常简单大家可以把自己的相关的函数对象传递进去试一下就明白了路子都是一样的没有什么难点。 但是这里面有一个问题它的返回值看上去有些奇怪std::invoke_result_t 有过相关开发经验的一眼就知道他一定是从std::invoke_result增加出来一个type类型实现的下面的就分析一下这个返回值。
三、std::invoke_result和std::result_of
其实在C11时就出现了std::result_of在C14又出现了std::result_of_t可是在C17基本就告诉你别用那个了哥给你提供了更好的std::invoke_result和std::invoke_result_t。这两个类型没别的其实就得得到当前函数的调用结果看名字也很容易明白。 看一下定义
template class
class result_of; // not definedtemplate class F, class... ArgTypes
class result_ofF(ArgTypes...);(1) (since C11)(deprecated in C17)(removed in C20)
template class F, class... ArgTypes
class invoke_result; (2) (since C17)它还有两个辅助类型
template class T
using result_of_t typename result_ofT::type;(1) (since C14)(deprecated in C17)(removed in C20)
template class F, class... ArgTypes
using invoke_result_t typename invoke_resultF, ArgTypes...::type;(2) (since C17)从上面的定义可以看出来result_of在C17就被抛弃了到了C20就完全被删除了。也就是说后面只能用invoke_result可这代码中要是有它就苦了编码的了。 下面看一个例子
#include functional
#include iostreamint testFunc() {std::cout test std::endl;return 0;
}
int testFuncArgs(int a, int b) {std::cout test ab a b std::endl;return a b;
}template typename F, typename... Args void TestArgs(F f, Args... args) {// static_assert(std::is_same_vstd::result_of_tF, int);static_assert(std::is_same_vstd::invoke_result_tF(), F);static_assert(std::is_same_vstd::invoke_result_tF, Args..., int);
}
template typename F void Test(F f) {static_assert(std::is_same_vstd::result_of_tF(), int);static_assert(std::is_same_vstd::invoke_result_tF(), F);static_assert(std::is_same_vstd::invoke_result_tF, int);
}int main() {Test(testFunc);TestArgs(testFuncArgs, 1, 2);
}
如果需要的话还可以std::decay_t, std::decay_t… args来处理具体的类型。 结果类型在普通的编程里其实真得没什么意义更多的应用是在模板编程和元编程中通过结果来处理某些情况同时利用结果的数据类型来达到某些定义使用。其实从标准的发展可以看到auto,decltype,delval等等都在不断的提供着类似的处理手段。所以重要的是灵活运用而不是拘泥于某种形式达到目的才是重点。
四、总结
C的变化是有目共睹的虽然在前两年特殊情况下导致一些新技术没有被应用到想象的版本中但在后续版本的迭代中仍然在不断的增加和完善。C学习的成本看似有些降低了但实际上可能增加的成本更多了。这并不矛盾新标准下编程可能更简单了但背后的逻辑却更复杂了新标准更容易理解了但新老标准的代码混合在一起更难了。 况且大多数的编程人员仍然使用C98C11的普及都没有想象的多更别提C14以后的标准了。不过不要着急慢慢来只要坚持学习就会达到彼岸毕竟学习成本比创造成本要低得多。C新版本好歹也要三年才升级一次。