全局命名空间带非类型模板参数的友元函数模板访问私有构造失败
问题分析与解决方案
报错原因
核心问题是命名空间的作用域解析歧义:
在ns::target类内声明友元时,如果直接写template<int N> friend target make();,编译器会默认把这个make解析为ns命名空间下的模板函数,而非全局命名空间的::make。因此全局的make模板并没有被授予访问target私有构造函数的权限,调用时自然触发权限错误。
而移除ns命名空间后,target和make处于同一全局作用域,友元声明能直接关联到正确的make模板,所以不会报错。
正确的友元声明方式
需要明确指定友元是全局命名空间的make模板,有两种可行写法:
写法一:先全局声明模板,再在类内关联
先在全局作用域提前声明make模板,再在ns::target里用全局作用域符号::明确引用它:
// 全局作用域提前声明模板 template<int N> ns::target make(); namespace ns { class target { private: target() = default; // 私有构造函数 // 明确指定友元是全局的make模板 template<int N> friend target ::make(); }; } // 全局模板的定义 template<int N> ns::target make() { return ns::target{}; }
写法二:类内直接声明全局模板为友元
不需要提前声明,直接在target的友元声明里用::限定make的作用域:
namespace ns { class target { private: target() = default; // 私有构造函数 // 直接声明全局命名空间的make模板为友元 template<int N> friend target ::make(); }; } // 全局模板的定义 template<int N> ns::target make() { return ns::target{}; }
两种写法都能让编译器正确识别需要授予友元权限的是全局的make模板,解决私有构造函数的访问问题,同时无需依赖显式特化,适配所有N的取值。
内容的提问来源于stack exchange,提问作者Takatoshi Kondo
相关产品推荐
相关产品推荐

