如何检查函数模板实例化的计数



如何检查函数模板实例化的计数?

template <typename T>
void foo(T f) {}

struct Bar {
void operator()(bool x) {}
};

void f1(bool x) {}

void f2(bool x) {}

int main() {
foo(f1);
foo(Bar());
foo(f2);
foo([](bool x){});
foo([](bool x){});
}

我认为这是(一种(谜题,假设OP是出于好奇而问的。

为了实现这一点,我打算向template foo()中添加一些内容,每个模板实例只调用一次。为此,可以选择构造一个静态实例。

解决方案的第二部分是让这些静态实例中的每一个都增加一个计数器。因此,我为静态实例使用了一个带有静态成员变量的类,构造函数通过该类递增计数器。

等等,我们开始了:

#include <iostream>
struct FooCounter {
static unsigned n;
FooCounter() { ++n; }
};
unsigned FooCounter::n = 0;
template <typename T>
void foo(T f)
{
static FooCounter count;
}

struct Bar {
void operator()(bool x) {}
};

void f1(bool x) {}

void f2(bool x) {}

int main() {
foo(f1);
foo(Bar());
foo(f2);
foo([](bool x){});
foo([](bool x){});
std::cout << "Counted " << FooCounter::n << " distinct template instances of foo().n";
}

输出:

Counted 4 distinct template instances of foo().

coliru上的实时演示


奖金部分:

4与我期望的模板实例数相匹配。不过,我必须承认,我在第2nd次浏览时意识到自己的错误,所以我错误地预期了4。

  • foo(f1)foo(f2)共享相同的模板实例,因为f1()f2()具有完全相同的签名
  • foo([](bool x){});的重复调用是每个调用都计数的调用。原因是每个lambda都有一个唯一的类型(由编译器内部生成(。(它们在字面上完全相同是无关紧要的。(因此,为它们中的每一个实例化template foo()

coliru上的增强演示


神圣的黑猫。。。

我刚刚了解到,即使在调用foo()的模板实例之前/没有调用它们,也可以对其进行改进,以计数它们。(它再次证明,几乎所有事情都可以通过另一种间接方法来解决。(

感谢@HolyBlackCat的演示:

#include <iostream>
int &FooCounter()
{
static int counter = 0;
return counter;
}
template <typename>
struct FooHelper
{
inline static int count = []{FooCounter()++; return 0;}();
};
template <auto> struct Use {};
template <typename T>
void foo(T f)
{
Use<&FooHelper<T>::count>{};
}

struct Bar {
void operator()(bool x) {}
};

void f1(bool x) {}

void f2(bool x) {}
using FooDouble = decltype(foo<double>); // <-- an explicit template instance
// which is never called but counted as well
int main() {
std::cout << "Counted " << FooCounter() << " distinct template instances of foo().n";
foo(f1);
foo(Bar());
foo(f2);
foo([](bool x){});
foo([](bool x){});
}

输出:

Counted 5 distinct template instances of foo().

coliru上的实时演示

请注意,即使是从未调用过的模板实例foo<double>()也会被计算在内。(由于FooCounter中的副作用,编译器可能不会对其进行优化,即使从未调用过它。(


经过思考,我想到了一个严肃的用例:它可以用于在工厂中自动注册类。

最新更新