我正在使用Curiously Recurring模板模式处理一些代码,其中派生类被传递以在编译时专门化它自己的基类。
有了这个,我遇到了一个烦人的问题,我已经做了一个玩具示例。我有一个带注释的base :: bar,其中包含描述预期行为的注释(以及我期望它的原因)。整个示例旨在按原样编译,并导致3个编译器错误(clang-3.9),如注释中所述。供参考,$ 4.11 / 2:
类型为“指向cv T类型B的成员的指针”的prvalue,其中B是类类型,可以转换为 类型为“指向cv T类型D的成员的指针”的prvalue,其中D是B的派生类(第10条)。如果B是 D的不可访问(第11条),模糊(10.2)或虚拟(10.1)基类,或虚拟的基类 D的基类,需要这种转换的程序是不正确的。转换的结果是指 转换发生前与指向成员的指针相同的成员,但它指的是基数 类成员,就好像它是派生类的成员一样。结果引用D的B实例中的成员。由于结果具有类型“指向cv T类型的D的成员的指针”,因此可以用D对象取消引用它。 结果与使用D的B子对象取消引用B成员的指针相同 null成员指针值被转换为目标类型的空成员指针值.57
#include <type_traits>
template<typename impl_t>
struct super
{
typedef int impl_t::* member_dat_t;
protected:
template<member_dat_t dat>
int foo()
{
return static_cast<impl_t *>(this)->*dat;
}
};
template<typename impl_t>
struct base : super<impl_t>
{
using this_t = base<impl_t>;
int base_dat = 0;
int bar()
{
// This, of course, succeeds
this-> template foo<&impl_t::derived_dat>();
// This fails during template instantiation, because the compiler knows that the
// location of base_dat/base_func is in the base<derived> subobject of a derived
// object rather than derived itself.
this-> template foo<&impl_t::base_dat>();
// (i.e., this succeeds, despite the address being taken on a member resolved off the impl_t)
static_assert(std::is_same<int this_t::*, decltype((&impl_t::base_dat))>::value, "");
// But what if we cast, as the standard (N3242) permits in $4.11/2 [conv.mem]
// Now, these succeed
static_assert(std::is_same<int impl_t::*, decltype((static_cast<int impl_t::*>(&impl_t::base_dat)))>::value, "");
static_assert(std::is_same<typename super<impl_t>::member_dat_t, decltype((static_cast<int impl_t::*>(&impl_t::base_dat)))>::value, "");
// But these still fail
this-> template foo<static_cast<int impl_t::*>(&impl_t::base_dat)>();
this-> template foo<static_cast<typename super<impl_t>::member_dat_t>(&impl_t::base_dat)>();
return 1;
}
};
struct derived : base<derived>
{
int derived_dat;
};
void test()
{
derived d;
d.bar();
}
对于“为什么”:base
中的现有代码仅使用super::foo
中实际定义的成员实例化derived
。如果我还可以使用由base<derived>
继承的derived
中定义的成员,我会很高兴。实际代码使用成员函数,但成员数据足以用于示例。
编辑:
我在技术上提出了一个应该有效的解决方案。基本上,base
来自super<base<impl_t>>
而不是super<impl_t>
,所以现在我们可以使用foo
的成员调用base<impl_t>
,如果我们需要不同的行为 - 派生类,我们可以使基础虚拟方法。但这会抛弃CRTP的一些好处(我们现在在对象中有VTBL,以及动态多态的成本,即使我们在编译时知道我们想要什么)。
由于