嵌套模板类SFINAE编译错误:成员函数类外定义匹配失败
嵌套模板类成员函数类外定义的SFINAE错误分析
问题背景
实现了graph模板类及其嵌套的node模板类,node仅在graph的模板参数上下文内定义,可通过现有node生成新node。使用SFINAE限制成员函数operation仅接受合法node类型,类内定义该函数可正常编译,但类外定义时出现编译错误。
代码示例
#include <type_traits> template <typename T> struct graph { template <typename U> class node; template <typename U> static constexpr std::true_type check_node(node<U> const&); static constexpr std::false_type check_node(...); template <typename V> static constexpr bool is_node_v = decltype(check_node(std::declval<V>()))::value; template <typename... Args> static constexpr bool are_nodes_v = (is_node_v<Args>&&...); }; template <typename T> template <typename U> struct graph<T>::node { // 类内定义可正常编译 // template <typename... Nodes, typename V = U, typename std::enable_if_t<graph<T>::template is_node_v<typename graph<T>::template node<V>> && graph<T>::template are_nodes_v<Nodes...>,V>* = nullptr> // auto operation(Nodes const&... nodes) -> node<V> // { // return node<V>(); // } // 类外声明 template <typename... Nodes, typename V = U, typename std::enable_if_t<graph<T>::template is_node_v<typename graph<T>::template node<V>> && graph<T>::template are_nodes_v<Nodes...>,V>* = nullptr> auto operation(Nodes const&... nodes) -> node<V>; }; // 类外定义(编译报错) template <typename T> template <typename U> template <typename... Nodes, typename V, typename Enable> auto graph<T>::node<U>::operation(Nodes const&... nodes) -> node<V> { return node<V>(); } int main() { auto gr = graph<int>(); auto nd = graph<int>::node<int>(); nd.operation(nd); }
编译器错误信息
<source>:37:6: error: no declaration matches 'graph<T>::node<V> graph<T>::node<U>::operation(const Nodes& ...)' 37 | auto graph<T>::node<U>::operation(Nodes const&... nodes) -> node<V> | ^~~~~~~~ <source>:26:6: note: candidate is: 'template<class T> template<class U> template<class ... Nodes, class V, std::enable_if_t<(graph<T>::is_node_v<node<V> > && graph<T>::are_nodes_v<Nodes ...>), V>* <anonymous> > graph<T>::node<V> graph<T>::node<U>::operation(const Nodes& ...)' 26 | auto operation(Nodes const&... nodes) -> node<V>; | ^~~~~~~~~ <source>:22:18: note: 'struct graph<T>::node<U>' defined here 22 | struct graph<T>::node | ^~~~ Compiler returned: 1
错误原因分析
类外定义的模板参数列表与类内声明不匹配,是导致编译错误的核心原因:
- 类内声明的
operation模板参数包含三个部分:- 可变参数
typename... Nodes - 带默认值的
typename V = U - 带默认值的特定类型参数
typename std::enable_if_t<...,V>* = nullptr
- 可变参数
- 类外定义时错误地将第三个参数替换为通用的
typename Enable,但编译器无法将这个通用类型与类内声明的std::enable_if_t<...>*类型匹配。 - 模板参数的顺序、类型、默认值必须与类内声明完全一致,否则编译器会认为这是一个全新的函数模板,而非类内声明的那个函数的定义。
修正方案
方案1:保持SFINAE写法,严格匹配类内模板参数
将类外定义的模板参数列表修改为与类内声明完全一致:
template <typename T> template <typename U> template <typename... Nodes, typename V = U, typename std::enable_if_t<graph<T>::template is_node_v<typename graph<T>::template node<V>> && graph<T>::template are_nodes_v<Nodes...>, V>* = nullptr> auto graph<T>::node<U>::operation(Nodes const&... nodes) -> node<V> { return node<V>(); }
方案2:使用C++20 requires约束替代SFINAE(更简洁)
如果支持C++20,用requires约束替换SFINAE,代码可读性更高,类外定义也更简单:
// 类内声明 template <typename... Nodes, typename V = U> auto operation(Nodes const&... nodes) -> node<V> requires (graph<T>::template is_node_v<graph<T>::template node<V>> && graph<T>::template are_nodes_v<Nodes...>); // 类外定义 template <typename T> template <typename U> template <typename... Nodes, typename V = U> auto graph<T>::node<U>::operation(Nodes const&... nodes) -> node<V> requires (graph<T>::template is_node_v<graph<T>::template node<V>> && graph<T>::template are_nodes_v<Nodes...>) { return node<V>(); }
内容的提问来源于stack exchange,提问作者taehyounpark
相关产品推荐
相关产品推荐

