如何将表达式扩展到可调用对象返回类型?参数类型扩展失败原因?
问题:通过可调用对象的返回类型实例化MyClass模板
我想要实现的功能是:通过createMyClass函数传入若干可调用对象,根据这些对象的返回类型来实例化MyClass模板,最终得到MyClass<return_type1, return_type2, ..., return_type_n>类型的实例。
我尝试用decltype(std::declval<Ts>()())获取可调用对象Ts的返回类型,并用折叠表达式(decltype(std::declval<Ts>()()), ...)来扩展,但编译时出现了以下语法错误:
syntax error: ',' was unexpected here
syntax error: unexpected token '...' following 'statement'
相关代码如下:
auto lambda = []() { return 'c'; }; template <typename ... Ts> struct MyClass { }; template <typename ... Ts> void createMyClass(Ts&& ... ts) { /* SUPPOSED TO CREATE MY CLASS WITH THE RETURN VALUES OF THE CALLABLES */ MyClass< (decltype(std::declval<Ts>()()), ...)> d; } int main() { createMyClass(lambda, lambda); return 0; }
请问如何正确将表达式扩展为可调用对象的返回类型?为何当前的参数类型扩展方式会报错?
解答
为什么当前写法报错
你用的(decltype(std::declval<Ts>()()), ...)是逗号运算符的折叠表达式,它的作用是依次执行每个decltype表达式,最终只保留最后一个表达式的结果——这和你想要的“把所有返回类型都作为模板参数”完全不符。而且模板参数列表需要的是多个类型组成的列表,这种逗号折叠的写法在语法上不被允许,编译器会把它解析成单个表达式,无法展开成多个类型参数,因此报错。
正确实现方式
不需要用折叠表达式,直接对每个可调用对象的返回类型做参数包展开即可,修正后的代码如下:
#include <utility> // 必须包含std::declval的头文件 auto lambda = []() { return 'c'; }; template <typename ... Ts> struct MyClass { }; template <typename ... Ts> void createMyClass(Ts&& ... ts) { // 直接展开每个Ts对应的返回类型作为MyClass的模板参数 MyClass<decltype(std::declval<Ts>()())...> d; } int main() { createMyClass(lambda, lambda); return 0; }
decltype(std::declval<Ts>()()):std::declval<Ts>()用于生成Ts类型的右值引用(不需要实际构造对象),调用其operator()后,decltype就能获取到该调用的返回类型。...直接跟在decltype(...)之后:这是C++参数包展开的标准写法,会把每个Ts对应的返回类型依次展开,最终MyClass的模板参数就是所有可调用对象的返回类型列表,比如传入两个返回char的lambda时,会实例化MyClass<char, char>。
内容的提问来源于stack exchange,提问作者Zebrafish
相关产品推荐
相关产品推荐

