在 C++ 中为模板的所有实例生成单个机器代码实例:是否可行 and/or 可能?

Single machine code instance generated for all instances of a template in C++: Is it feasible and/or possible?

我一直在考虑这个问题,我想我在某处读到过,但我找不到更多关于这个的信息。

仅出于理论而非实际目的,是否可能、可行、and/or 实际生成单个 模板实例,能够处理所有实例这样的模板?有没有编译器能够做到这一点?

例如,考虑这个 class 模板...

template<typename T>
class UselessCalculator {
    private:
        T   result;

    public:
        UselessCalculator() : result(0) {}

        UselessCalculator &operator=(T what) {
            this->result = what;
            return *this;
        }

        UselessCalculator &operator+=(T what) {
            this->result += what;
            return *this;
        }

        UselessCalculator &operator-=(T what) {
            this->result -= what;
            return *this;
        }

        UselessCalculator &operator*=(T what) {
            this->result *= what;
            return *this;
        }

        UselessCalculator &operator/=(T what) {
            this->result /= what;
            return *this;
        }
};

对于任何 TUselessCalculator 都有一些要求列表,可以用作它的模板参数,就像概念一样。在这种情况下,要求是:

现在,按照这个愚蠢的 "single instance fits all" 想法,这将如何 实施 ?我认为下面的 C 代码可能说明了一种可能性...

struct UselessCalculatorTemplateVirtualTable {
    struct  someFunkyImplementationOfStdTypeInfo *type;
    void    (*constructInt)(void*, int);
    void    (*copyConstruct)(void*, const void*);
    void    (*moveConstruct)(void*, void*);
    void    (*destruct)(void*);
    void    (*operatorAddAssign)(void*, const void*);
    void    (*operatorSubtractAssign)(void*, const void*);
    void    (*operatorMultiplyAssign)(void*, const void*);
    void    (*operatorDivideAssign)(void*, const void*);
};

// I won't repeat that long name all over the place...
typedef struct UselessCalculatorTemplateVirtualTable VirtualTable;

void UselessCalculatorConstruct(VirtualTable *table, void *this) {
    table->constructInt(this, 0);
}

void UselessCalculatorCopy(VirtualTable *table, void *this, const void *what) {
    table->copyConstruct(this, what);
}

void UselessCalculatorMove(VirtualTable *table, void *this, void *what) {
    table->moveConstruct(this, what);
}

void UselessCalculatorDestruct(VirtualTable *table, void *this) {
    table->destruct(this);
}

void UselessCalculatorAddAssign(VirtualTable *table, void *this, void *what) {
    table->operatorAddAssign(this, what);
}

void UselessCalculatorAddAssign(VirtualTable *table, void *this, void *what) {
    table->operatorSubtractAssign(this, what);
}

void UselessCalculatorMultiplyAssign(VirtualTable *table, void *this, void *what) {
    table->operatorMultiplyAssign(this, what);
}

void UselessCalculatorDivideAssign(VirtualTable *table, void *this, void *what) {

    table->operatorDivideAssign(this, what);
}

现在,编译器必须为每个 UselessCalculator<T> "instantiate" 做的唯一事情是 VirtualTable 和辅助函数(如果有的话)。例如,UselessCalculator<int> 将转换为...

#define real(what) ((int*)what)

void constructInt(void *this, int what) {
    *real(this) = what;
}

void copyConstruct(void *this, const void *what) {
    *real(this) = *real(what);
}

void moveConstruct(void *this, void *what) {
    *real(this) = *real(what);
}

void destruct(void *this) {}

void operatorAddAssign(void *this, const void *what) {
    *real(this) += *real(what);
}


void operatorSubtractAssign(void *this, const void *what) {
    *real(this) -= *real(what);
}


void operatorMultiplyAssign(void *this, const void *what) {
    *real(this) *= *real(what);
}


void operatorDivideAssign(void *this, const void *what) {
    *real(this) /= *real(what);
}

然后,考虑到这一点...

int main() {
    UselessCalculator<int> myUselessCalc;
    myUselessCalc += 10;
    myUselessCalc *= 10;
    myUselessCalc -= 10;
    myUselessCalc /= 10;
}

VirtualTable virtualTableInt = {
    &someFunkyImplementationOfStdTypeInfoForInt,
    constructInt,
    copyConstruct,
    moveConstruct,
    destroy,
    addAssign,
    subtractAssign,
    multiplyAssign,
    divideAssign
    };

可以翻译成这个 C 代码...(不考虑例外情况!)

struct UselessCalculatorInt {
    int result;
};

int main() {
    int tmpStorage;

    UselessCalculatorInt myUselessCalc;
    UselessCalculatorConstruct(&virtualTableInt, &myUselessCalc);

    tmpStorage = 10;
    UselessCalculatorAddAssign(&virtualTableInt, &myUselessCalc, &tmpStorage);

    tmpStorage = 10;
    UselessCalculatorSubtractAssign(&virtualTableInt, &myUselessCalc, &tmpStorage);

    tmpStorage = 10;
    UselessCalculatorMultiplyAssign(&virtualTableInt, &myUselessCalc, &tmpStorage);

    tmpStorage = 10;
    UselessCalculatorDivideAssign(&virtualTableInt, &myUselessCalc, &tmpStorage);

    UselessCalculatorDestroy(&virtualTableInt, &myUselessCalc);
    return 0;
}

我知道这会打败模板的所有用途,很多人不会喜欢这个想法(我不喜欢,我只是好奇),而且代码可能效率较低, CPU- 和内存方面,更不用说它几乎是不可优化的,它们将不再是 "templates"。但是其他人 ab 过去用得更糟,不是吗? ;). 顺便说一句,最好有一个开关来禁用它,如果曾经实施过,只要有必要,因为模板元编程将变得几乎无用。

所以,我的问题是,如果还不够清楚,所有这些乱七八糟的东西是否可行、实用、可实施?是否有一些工具链成功地做到了这一点?它会提供任何可以超过其明显开销的好处吗?

好吧,您基本上是在问是否某些编译器可以通过 运行 时间多态性("classic" OOP with virtual functions)实现通常称为 "compile-time polymorphism"(C++ 模板)的东西。

虽然这在理论上可能是可行的(至少在某种程度上是有限的),但如果真的违背了语言设计原则。在编译时多态性更合适的情况下,专门提供模板作为 运行 时多态性的有效替代。模板的全部目的是不同于 "ordinary" 运行 时间多态性。许多模板功能严重依赖于它们的编译时性质。

想法是,如果您使用模板,则意味着您想要编译时多态性,而不是运行时多态性。

这是奖章的一面。另一方面,编写不当的模板代码会导致不必要的代码膨胀,因为在 运行 时间多态性很容易实现相同目标的情况下,会强制执行编译时多态性(即相同代码的重复实例化)更少的代码膨胀和可忽略的性能损失。从这个角度来看,隐式自动切换到模板代码中的 运行 时间多态性可能是有益的。但我相信当前状态下的语言并不是为此量身定做的。这是您应该明确自己做的事情。