不完全输入好友功能
Incomplete type in friend function
这是我的代码的一个mcve:(如果重要的话,Options_proxy
和Options
有构造函数)。我知道这还远远不够简单,但不能再简化了,同时仍然显示错误:
template <class Impl>
struct Options_proxy : Impl {
using Flag = typename Impl::Flag;
friend constexpr auto operator!(Flag f) -> Options_proxy {
return {}; // <-- error here
};
};
template <class Impl>
struct Options : Impl {
using Flag = typename Impl::Flag;
};
struct File_options_impl {
enum class Flag : unsigned { nullflag, read, write };
friend constexpr auto operator!(Flag f) -> Options_proxy<File_options_impl>;
};
using File_options = Options<File_options_impl>;
auto foo()
{
!File_options::Flag::write; // <-- required from here
}
gcc 6和7给出这个错误:
In instantiation of 'constexpr Options_proxy<File_options_impl> operator!(Options_proxy<File_options_impl>::Flag)':
required from ... etc etc...
error: return type 'struct Options_proxy<File_options_impl>' is incomplete
clang编译OK
如果:
- 移除
operator!
中的constexpr
或
- 在operator call前添加一个类型为
Options_proxy<File_options_impl>
的对象:
:
auto foo()
{
Options_proxy<File_options_impl> o;
!File_options::Flag::write; // <-- now OK in gcc also
}
这是一个gcc bug还是代码中的一些未定义行为,如未指定或不需要诊断?
关于写这段代码的动机:
我想创建一个类型安全的标志/选项系统(不含宏):图书馆黑魔法:
template <class Impl>
requires Options_impl<Impl>
struct Options : Impl {
// go crazy
};
用户代码:struct File_options_impl {
// create a system where here the code
// needs to be as minimal as possible to avoid repetition and user errors
// this is what the user actually cares about
enum class Flag : unsigned { nullflag = 0, read = 1, write = 2, create = 4};
// would like not to need to write this,
// but can't find a way around it
friend constexpr auto operator!(Flag f1) -> Options_proxy<File_options_impl>;
friend constexpr auto operator+(Flag f1, Flag f2) -> Options_proxy<File_options_impl>;
friend constexpr auto operator+(Flag f1, Options_proxy<File_options_impl> f2) -> Options_proxy<File_options_impl>;
};
using File_options = Options<File_options_impl>;
然后:
auto open(File_options opts);
using F_opt = File_options::Flag;
open(F_opt::write + !F_opt::create);
看起来这是一个gcc bug。下面是一个MCVE(4行):
struct X;
template<int> struct A { friend constexpr A f(X*) { return {}; } };
// In instantiation of 'constexpr A<0> f(X*)':
// error: return type 'struct A<0>' is incomplete
struct X { friend constexpr A<0> f(X*); };
auto&& a = f((X*)0);
clang和MSVC接受。
正如您所观察到的,gcc接受没有constexpr
的相同程序,或者如果您在auto&& a = f((X*)0);
之前显式实例化A<0>
(例如使用template struct A<0>;
)。这表明gcc遇到的问题是在类模板隐式实例化[temp.inst]:
1 -除非类模板特化已经被显式实例化(14.7.2)或显式特化(14.7.3),当在上下文中引用类模板专门化时,将隐式实例化该类模板专门化这需要一个完全定义的对象类型,或者当类类型的完整性影响语义时
类模板A<0>
在constexpr A<0> f(X*)
的return
语句中是必需的,因此应该在此时隐式实例化。虽然友元函数定义在词法上位于类A
中,但在友元函数的定义中,不应认为类是不完整的;例如,以下非模板程序被普遍接受:
struct Y;
struct B { friend constexpr B f(Y*) { return {}; } };
struct Y { friend constexpr B f(Y*); };
auto&& b = f((Y*)0);
有趣的是,gcc 和 clang(虽然不是MSVC)在使用以下程序时都有问题(同样,通过删除constexpr
或显式实例化template struct C<0>;
来修复):struct Z;
template<int> struct C { friend constexpr C* f(Z*) { return 0; } };
struct Z { friend constexpr C<0>* f(Z*); };
// error: inline function 'constexpr C<0>* f(Z*)' used but never defined
auto&& c = f((Z*)0);
我建议使用显式实例化方法。在您的例子中,它将是:
template class Options_proxy<File_options_impl>;
我在你的代码中看到的一个问题是,多个实现可能有相同的::Flag成员,这意味着你的朋友操作符可能被定义多次,违反了一个定义规则。
Options_proxy也没有任何私有成员,所以你不需要将操作符设为友元(我认为你滥用友元来定义内联的外部函数)。
你需要一个明确的操作符定义,我认为当前的签名是不可能的。
如果保证标志是唯一的,可以尝试将操作符移到Options_proxy之外。
template <class Impl>
struct Options_proxy : Impl {
using Flag = typename Impl::Flag;
};
template <class Impl, typename Flag = Impl::Flag>
constexpr Options_proxy<Impl> operator!(Flag f) {
return {};
}
template <class Impl>
struct Options : Impl {
using Flag = typename Impl::Flag;
};
struct File_options_impl {
enum class Flag : unsigned { nullflag, read, write };
friend constexpr Options_proxy<File_options_impl> operator!(Flag f);
// Or if that doesn't work:
friend constexpr Options_proxy<File_options_impl> operator!<File_options_impl>(Flag f);
};
- 有没有可能有一个只有ADL才能找到的非好友功能
- 解析模板的好友功能时出现问题
- 特定好友功能专业化
- 好友功能 - 成员无法访问
- 无法从好友功能访问类的私有成员?"ostream"不是"std"的成员?
- 好友功能仍无法访问私人会员
- CUDA C++中的好友功能
- 我无法获得好友会员功能以实际访问私人会员
- 好友功能没有访问私人成员
- 我无法正确使用好友功能
- 如何更恰当地处理好友功能
- 为什么我的好友功能无法访问私人会员?
- 好友功能和受保护的数据
- 选择何时使用好友功能
- 好友功能"non-lvalue in assignment"
- 好友功能无法访问私人数据成员
- 在C++中使用好友功能有什么额外的优势?
- 成员无法使用好友功能访问
- 即使将会员功能设为好友也无法访问会员
- 好友功能 - 无法访问私人字段C++