以下代码如何工作,以便每次为唯一的调用堆栈唯一地实例化模板函数?

时间:2018-09-21 09:49:08

标签: c++ templates lambda

我从Unreal Engine的源中遇到了以下代码

namespace UE4Asserts_Private
{
    // This is used by ensure to generate a bool per instance
    // by passing a lambda which will uniquely instantiate the template.
    template <typename Type>
    bool TrueOnFirstCallOnly(const Type&)
    {
        static bool bValue = true;
        bool Result = bValue;
        bValue = false;
        return Result;
    }

    FORCEINLINE bool OptionallyDebugBreakAndPromptForRemoteReturningFalse(bool bBreak, bool bIsEnsure = false)
    {
        if (bBreak)
        {
            FPlatformMisc::DebugBreakAndPromptForRemoteReturningFalse(bIsEnsure);
        }
        return false;
    }
}

#define ensure(           InExpression                ) (LIKELY(!!(InExpression)) || FDebug::OptionallyLogFormattedEnsureMessageReturningFalse(UE4Asserts_Private::TrueOnFirstCallOnly([]{}), #InExpression, __FILE__, __LINE__, TEXT("")               ) || UE4Asserts_Private::OptionallyDebugBreakAndPromptForRemoteReturningFalse(UE4Asserts_Private::TrueOnFirstCallOnly([]{}), true))

现在,每当我们使用ensure(SomeExpression)时,UE4Asserts_Private::TrueFirstCallOnly的{​​{1}}参数仅在它第一次被特定的调用堆栈调用时才为true(我认为每个调用堆栈都需要,因为FDebug::OptionallyLogFormattedEnsureMessageReturningFalse在下一次调用时评估为false以确保来自同一调用栈的确保,但从另一个调用栈触发了sure但不是很确定),我不知道这是如何工作的。

正如他们在评论中所述,以某种方式将lambda TrueOnFirstCallOnly传递给模板函数会对其进行唯一实例化。它是如何工作的?作为模板传递的lambda真正独特的是什么,是调用堆栈还是其他?

如果表达式为真,可以认为

[]{}为真

1 个答案:

答案 0 :(得分:1)

这是可以实现这样的true_on_first_call的方式:

include <iostream>

template <typename T> struct true_on_first_call {
    static bool first;
    bool operator()() {
        if (first) {
            first = false;
            return true;
        }
        return false;
    }
};
template <typename T> bool true_on_first_call<T>::first = true;
template <typename T> 
bool get_true_on_first_call(const T &){ return true_on_first_call<T>()(); }

void foo() {
    std::cout << get_true_on_first_call([]{}) << "\n";    // instantiation for []{}
}
void bar() {
    std::cout << get_true_on_first_call([]{}) << "\n";    // instantiation for []{}
}                                                         // note: its a different type 
                                                          // than the []{} above!
                                                          // but the same on 
                                                          // repeated calls to foo


int main() {
    std::cout << "first \n";
    foo();
    bar();
    std::cout << "second \n";
    foo();
    bar();
}

Live demo

诀窍在于,每个labmda表达式都具有唯一的类型,因此它将导致true_on_first_call的不同实例化。即使Lambda表达式相同([]{}[]{}),它们的类型也不同。另一方面,相同的lambda表达式(即foo的第一次调用的那个和foo的第二次调用的那个)具有相同的类型。这样,您每次编写get_true_on_first_call([]{})时都可以获得唯一的实例。