比我一开始以为的要困难许多,不知道有无更简单的方法。

来看一个C++填空题:

// 完善此宏,使下面的断言成立
#define DEFINE_SELF ?
struct A { DEFINE_SELF };
struct B { DEFINE_SELF };
static_assert(std::is_same_v<A::Self, A>);
static_assert(std::is_same_v<B::Self, B>);

一个比较直观的想法是利用this的类型,把Self提取出来:

#define DEFINE_SELF using Self = std::remove_pointer_t<decltype(this)>;

让我们看看llvm-cl怎么说:

error : invalid use of 'this' outside of a non-static member function

我尝试了很多次,如用非静态成员函数做中转、从成员函数指针萃取等,都宣告失败;简单地google一下,得到的也是满屏幕的“寄”。

不过,我最终还是在某个犄角旮旯里发现了一个repo(链接),虽然没什么star,却确实地解决了问题。

以下是简化过的代码实现,在我的渲染器Rtrc中有使用:

template<typename T>
struct SelfTypeReader
{
    friend auto GetSelfTypeADL(SelfTypeReader);
};
 
template<typename T, typename U>
struct SelfTypeWriter
{
    friend auto GetSelfTypeADL(SelfTypeReader<T>) { return U{}; }
};
 
void GetSelfTypeADL();
 
template<typename T>
using SelfTypeResult = std::remove_pointer_t<decltype(GetSelfTypeADL(SelfTypeReader<T>{}))>;
 
#define DEFINE_SELF                                                         \
    struct SelfTypeTag{};                                                   \
    constexpr auto SelfTypeHelper() ->                                      \
        decltype(SelfTypeWriter<SelfTypeTag, decltype(this)>{}, void()) { } \
    using Self = SelfTypeResult<SelfTypeTag>;

之前提到过,我一直没找到怎么把decltype(this)利用起来,问题的核心在于没法静态地访问this。那么这段代码的思路就是利用auto函数返回值类型可以被延迟推导的特点,在一个可以访问this的地方把decltype(this)给记录到某个外部函数的返回类型上。

首先,SelfTypeReader声明了一个函数auto GetSelfTypeADL(SelfTypeReader),此时我们还不知道它的返回类型是什么。接着,DEFINE_SELF在一个可以合法地使用this的地方把decltype(this)塞给了SelfTypeWriter,它内部提供了auto GetSelfTypeADL(SelfTypeReader)的实现,将decltype(this)GetSelfTypeADL返回类型的方式记录下来。最后,我们借助之前定义的SelfTypeReader拿到需要的结果。

SelfTypeTag的存在只是为了确保不同对每个不同的类型,都有唯一对应的SelfTypeReader/SelfTypeWriter

这套做法令人十分眼熟……仔细一想,这不就是臭名昭著的Stateful Metaprogramming吗……

C++,很奇妙吧~