C++ 递归变量函数模板返回类型的decltype
给定以下代码(取自):C++ 递归变量函数模板返回类型的decltype,c++,c++11,variadic-templates,c++14,decltype,C++,C++11,Variadic Templates,C++14,Decltype,给定以下代码(取自): #包括 #包括 #包括 #包括 #包括 #包括 模板 结构组合\u impl { compose_impl(Fs&&…Fs):functionTuple(std::forward_as_tuple(Fs…){ 模板 自动应用(标准:积分常数,Ts&&…Ts)常数 { 返回apply(std::integral_constant(),std::get(functionTuple)(std::forward(ts)…); } 模板 自动应用(标准:积分常数,Ts&&…Ts)常
#包括
#包括
#包括
#包括
#包括
#包括
模板
结构组合\u impl
{
compose_impl(Fs&&…Fs):functionTuple(std::forward_as_tuple(Fs…){
模板
自动应用(标准:积分常数,Ts&&…Ts)常数
{
返回apply(std::integral_constant(),std::get(functionTuple)(std::forward(ts)…);
}
模板
自动应用(标准:积分常数,Ts&&…Ts)常数
{
返回std::get(functionTuple)(std::forward(ts)…);
}
模板
自动运算符()(Ts&&…Ts)常量
{
返回应用(std::integral_constant(),std::forward(ts)…);
}
std::tuple函数tuple;
};
模板
自动合成(Fs&&…Fs)
{
返回compose_impl(std::forward(fs)…);
}
int main()
{
auto f1=[](std::pair p){返回p.first+p.second;};
自动f2=[](双x){return std::make_pair(x,x+1.0);};
自动f3=[](双x,双y){返回x*y;};
自动g=合成(f1、f2、f3);
std::cout第一个选项的错误消息是由于
std::declval<func_type>()(std::forward<Ts>(ts)...)
注:
编译并在GC4.4.1和Clang 3.5.0中使用C++ 11模式,在Visual C++ 2013上使用。
- 如前所述,
ret_hlp
仅处理声明其运算符()
的函数对象类型,类似于lambda闭包类型,但它可以轻松扩展到几乎任何其他类型,包括普通函数类型
- 我试图尽可能少地更改原始代码;我认为关于该代码有一点需要提及:if
compose
被赋予左值参数(如本例所示),functionTuple
内部的compose\u impl
将存储对这些参数的引用。这意味着只要使用复合函子,原始函子就必须可用,否则将有悬空引用
编辑:根据评论中的要求,这里有关于最后一个注释的更多信息:
这种行为是由于转发引用的工作方式造成的,即Fs&&…
函数参数的compose
。如果您有一个F&&
形式的函数参数,正在对其进行模板参数推导(如下所示),并且为该参数提供了一个a
类型的参数,则:
- 如果参数表达式是右值,
F
被推断为A
,当替换回函数参数时,它给出A&&
(例如,如果将lambda表达式直接作为参数传递给compose
),则会发生这种情况
- 如果参数表达式是一个左值,
F
被推导为A&
,当替换回函数参数时,它给出A&&
,根据规则产生A&
(这是当前示例中的情况,因为f1
和其他都是左值)
因此,在当前示例中,compose\u impl
将使用推断的模板参数进行实例化,如下所示(使用lambda闭包类型的发明名称)
如果将lambda表达式作为参数直接传递给compose
,那么根据上面的说明,functionTuple
将具有以下类型
std::tuple<lambda_1_type&, lambda_2_type&, lambda_3_type&>
std::tuple<lambda_1_type, lambda_2_type, lambda_3_type>
结果是函数对象将始终被复制或移动到元组中,因此即使在原始组件被破坏之后,也可以使用生成的组合函数对象
哇,这太长了;也许你不应该说“精心设计”:)
编辑2作为OP的第二条注释:是的,原样的代码,没有std::decay
(但正如您所说,扩展为正确确定普通函数参数的ret_type
)将处理普通函数,但要小心:
int f(int) { return 7; }
int main()
{
auto c1 = compose(&f, &f); //Stores pointers to function f.
auto c2 = compose(f, f); //Stores references to function f.
auto pf = f; //pf has type int(*)(int), but is an lvalue, as opposed to &f, which is an rvalue.
auto c3 = compose(pf, pf); //Stores references to pointer pf.
std::cout << std::is_same<decltype(c1.functionTuple), std::tuple<int(*)(int), int(*)(int)>>::value << '\n';
std::cout << std::is_same<decltype(c2.functionTuple), std::tuple<int(&)(int), int(&)(int)>>::value << '\n';
std::cout << std::is_same<decltype(c3.functionTuple), std::tuple<int(*&)(int), int(*&)(int)>>::value << '\n';
}
intf(int){return 7;}
int main()
{
auto c1=compose(&f,&f);//存储函数f的指针。
auto c2=compose(f,f);//存储对函数f的引用。
auto-pf=f;//pf的类型为int(*)(int),但它是一个左值,而&f是一个右值。
auto c3=compose(pf,pf);//存储对指针pf的引用。
std::我可以将此标记为已接受,因为它将我引向了正确的方向。您能详细说明一下第三条注释吗?您看到了更好的实现方法吗?谢谢您详细说明:)。作为补充说明,它的实现,以及返回类型的适当函数特性,也处理普通函数。
#include <cstddef>
#include <type_traits>
#include <tuple>
#include <iostream>
#include <utility>
#include <functional>
template<typename> struct ret_hlp;
template<typename F, typename R, typename... Args> struct ret_hlp<R (F::*)(Args...) const>
{
using type = R;
};
template<typename F, typename R, typename... Args> struct ret_hlp<R (F::*)(Args...)>
{
using type = R;
};
template<typename ... Fs>
struct compose_impl
{
compose_impl(Fs&& ... fs) : functionTuple(std::forward_as_tuple(fs ...)) {}
using f1_type = typename std::remove_reference<typename std::tuple_element<0, std::tuple<Fs...>>::type>::type;
using ret_type = typename ret_hlp<decltype(&f1_type::operator())>::type;
template<size_t N, typename ... Ts>
ret_type apply(std::integral_constant<size_t, N>, Ts&& ... ts) const
{
return apply(std::integral_constant<size_t, N - 1>(), std::get<N> (functionTuple)(std::forward<Ts>(ts)...));
}
template<typename ... Ts>
ret_type apply(std::integral_constant<size_t, 0>, Ts&& ... ts) const
{
return std::get<0>(functionTuple)(std::forward<Ts>(ts)...);
}
template<typename ... Ts>
ret_type operator()(Ts&& ... ts) const
{
return apply(std::integral_constant<size_t, sizeof ... (Fs) - 1>(), std::forward<Ts>(ts)...);
}
std::tuple<Fs ...> functionTuple;
};
template<typename ... Fs>
compose_impl<Fs ...> compose(Fs&& ... fs)
{
return compose_impl<Fs ...>(std::forward<Fs>(fs) ...);
}
int main ()
{
auto f1 = [](std::pair<double,double> p) {return p.first + p.second; };
auto f2 = [](double x) {return std::make_pair(x, x + 1.0); };
auto f3 = [](double x, double y) {return x*y; };
auto g = compose(f1, f2, f3);
std::cout << g(2.0, 3.0) << std::endl; //prints '13', evaluated as (2*3) + ((2*3)+1)
return 0;
}
compose_impl<lambda_1_type&, lambda_2_type&, lambda_3_type&>
std::tuple<lambda_1_type&, lambda_2_type&, lambda_3_type&>
std::tuple<lambda_1_type, lambda_2_type, lambda_3_type>
std::tuple<typename std::decay<Fs>::type ...> functionTuple;
int f(int) { return 7; }
int main()
{
auto c1 = compose(&f, &f); //Stores pointers to function f.
auto c2 = compose(f, f); //Stores references to function f.
auto pf = f; //pf has type int(*)(int), but is an lvalue, as opposed to &f, which is an rvalue.
auto c3 = compose(pf, pf); //Stores references to pointer pf.
std::cout << std::is_same<decltype(c1.functionTuple), std::tuple<int(*)(int), int(*)(int)>>::value << '\n';
std::cout << std::is_same<decltype(c2.functionTuple), std::tuple<int(&)(int), int(&)(int)>>::value << '\n';
std::cout << std::is_same<decltype(c3.functionTuple), std::tuple<int(*&)(int), int(*&)(int)>>::value << '\n';
}