向成员的下行指针作为模板参数

时间:2017-03-20 01:07:20

标签: c++ templates casting member-pointers

我正在使用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,以及动态多态的成本,即使我们在编译时知道我们想要什么)。

由于

0 个答案:

没有答案