class 定义中的模板参数类型推导
Template argument type deduction from within the class definition
是否可以在 C
的成员函数之一的定义中将 class template argument deduction 用于 class C
? ...或者我是否被迫像在 C++03 中那样编写我的 make_c
助手 class?
考虑这个构建任意函数对象链的最小化和简化场景:
template <typename F>
struct node;
template <typename FFwd>
node(FFwd&&) -> node<std::decay_t<FFwd>>;
node
class 存储了一个通过完美转发初始化的函数对象。我需要一个 推导指南 这里 decay
函数的类型
对象。
template <typename F>
struct node
{
F _f;
template <typename FFwd>
node(FFwd&& f) : _f{std::forward<FFwd>(f)}
{
}
template <typename FThen>
auto then(FThen&& f_then)
{
return node{[f_then = std::move(f_then)]
{
return f_then();
}};
}
};
之后,我定义了node
的构造函数和一个.then
延续成员函数,returns一个新节点(它的实现对于最小化大小是无意义的示例)。如果我尝试调用 .then
...
auto f = node{[]{ return 0; }}.then([]{ return 0; });
...我收到意外的编译错误:
prog.cc: In instantiation of 'node<F>::node(FFwd&&) [with FFwd = node<F>::then(FThen&&) [with FThen = main()::<lambda()>; F = main()::<lambda()>]::<lambda()>; F = main()::<lambda()>]':
prog.cc:27:22: required from 'auto node<F>::then(FThen&&) [with FThen = main()::<lambda()>; F = main()::<lambda()>]'
prog.cc:35:56: required from here
prog.cc:17:46: error: no matching function for call to 'main()::<lambda()>::__lambda1(<brace-enclosed initializer list>)'
node(FFwd&& f) : _f{std::forward<FFwd>(f)}
^
prog.cc:35:20: note: candidate: 'constexpr main()::<lambda()>::<lambda>(const main()::<lambda()>&)'
auto f = node{[]{ return 0; }}.then([]{ return 0; });
^
发生这种情况是因为在 node<F>::then
的主体内,node{...}
创建了一个 *this
类型的实例 - 它不会触发参数类型推导。因此我不得不写:
template <typename FThen>
auto then(FThen&& f_then)
{
auto l = [f_then = std::move(f_then)]{ return f_then(); };
return node<std::decay_t<decltype(l)>>{std::move(l)};
}
...这违背了演绎指南的全部目的。
有没有一种方法可以在这里使用 class 模板参数推导而不引入代码重复或 make_node
函数?
我找到了一个可能的解决方案,但它确实需要 make
函数的外部实现。
好消息:它不一定是 make_node
- 它可以与任何支持 class 模板参数推导的类型 T
一起使用。
template <template <typename...> class T, typename... Ts>
auto make(Ts&&... xs)
noexcept(noexcept(T{std::forward<Ts>(xs)...}))
-> decltype(T{std::forward<Ts>(xs)...})
{
return T{std::forward<Ts>(xs)...};
}
用法:
template <typename FThen>
auto then(FThen&& f_then)
{
return make<node>([f_then = std::move(f_then)]
{
return f_then();
});
}
node
的名称查找找到了注入的-class-名称,未从中执行推导。 (在这种情况下执行推导会破坏向后兼容性。)
如果要推导,请限定名称以便找到命名空间成员。
template <typename FThen>
auto then(FThen&& f_then)
{
return ::node{[f_then = std::move(f_then)]
// ^^
{
return f_then();
}};
}
此外,更简洁的指南编写方式是
template <typename F>
node(F) -> node<F>;
是否可以在 C
的成员函数之一的定义中将 class template argument deduction 用于 class C
? ...或者我是否被迫像在 C++03 中那样编写我的 make_c
助手 class?
考虑这个构建任意函数对象链的最小化和简化场景:
template <typename F>
struct node;
template <typename FFwd>
node(FFwd&&) -> node<std::decay_t<FFwd>>;
node
class 存储了一个通过完美转发初始化的函数对象。我需要一个 推导指南 这里 decay
函数的类型
对象。
template <typename F>
struct node
{
F _f;
template <typename FFwd>
node(FFwd&& f) : _f{std::forward<FFwd>(f)}
{
}
template <typename FThen>
auto then(FThen&& f_then)
{
return node{[f_then = std::move(f_then)]
{
return f_then();
}};
}
};
之后,我定义了node
的构造函数和一个.then
延续成员函数,returns一个新节点(它的实现对于最小化大小是无意义的示例)。如果我尝试调用 .then
...
auto f = node{[]{ return 0; }}.then([]{ return 0; });
...我收到意外的编译错误:
prog.cc: In instantiation of 'node<F>::node(FFwd&&) [with FFwd = node<F>::then(FThen&&) [with FThen = main()::<lambda()>; F = main()::<lambda()>]::<lambda()>; F = main()::<lambda()>]':
prog.cc:27:22: required from 'auto node<F>::then(FThen&&) [with FThen = main()::<lambda()>; F = main()::<lambda()>]'
prog.cc:35:56: required from here
prog.cc:17:46: error: no matching function for call to 'main()::<lambda()>::__lambda1(<brace-enclosed initializer list>)'
node(FFwd&& f) : _f{std::forward<FFwd>(f)}
^
prog.cc:35:20: note: candidate: 'constexpr main()::<lambda()>::<lambda>(const main()::<lambda()>&)'
auto f = node{[]{ return 0; }}.then([]{ return 0; });
^
发生这种情况是因为在 node<F>::then
的主体内,node{...}
创建了一个 *this
类型的实例 - 它不会触发参数类型推导。因此我不得不写:
template <typename FThen>
auto then(FThen&& f_then)
{
auto l = [f_then = std::move(f_then)]{ return f_then(); };
return node<std::decay_t<decltype(l)>>{std::move(l)};
}
...这违背了演绎指南的全部目的。
有没有一种方法可以在这里使用 class 模板参数推导而不引入代码重复或 make_node
函数?
我找到了一个可能的解决方案,但它确实需要 make
函数的外部实现。
好消息:它不一定是 make_node
- 它可以与任何支持 class 模板参数推导的类型 T
一起使用。
template <template <typename...> class T, typename... Ts>
auto make(Ts&&... xs)
noexcept(noexcept(T{std::forward<Ts>(xs)...}))
-> decltype(T{std::forward<Ts>(xs)...})
{
return T{std::forward<Ts>(xs)...};
}
用法:
template <typename FThen>
auto then(FThen&& f_then)
{
return make<node>([f_then = std::move(f_then)]
{
return f_then();
});
}
node
的名称查找找到了注入的-class-名称,未从中执行推导。 (在这种情况下执行推导会破坏向后兼容性。)
如果要推导,请限定名称以便找到命名空间成员。
template <typename FThen>
auto then(FThen&& f_then)
{
return ::node{[f_then = std::move(f_then)]
// ^^
{
return f_then();
}};
}
此外,更简洁的指南编写方式是
template <typename F>
node(F) -> node<F>;