指向(数据)成员的指针,作为非类型模板参数,例如具有自动存储期限/无链接

问题描述 投票:4回答:1

请考虑以下代码段:

#include <cstdint>
#include <iostream>

struct Foo {
    Foo() : foo_(0U), bar_(0U) {}

    void increaseFoo() { increaseCounter<&Foo::foo_>(); }
    void increaseBar() { increaseCounter<&Foo::bar_>(); }

    template <uint8_t Foo::*counter>
    void increaseCounter() { ++(this->*counter); }

    uint8_t foo_;
    uint8_t bar_;
};

void callMeWhenever() {
    Foo f;  // automatic storage duration, no linkage.
    f.increaseFoo();
    f.increaseFoo();
    f.increaseBar();

    std::cout << +f.foo_ << " " << +f.bar_;  // 2 1
}

int main() {
    callMeWhenever();
}

[我的第一个猜测是格式错误,因为f中的callMeWhenever()具有自动存储时间,并且在编译时不知道其地址,而increaseCounter()的成员模板函数Foo用指向Foo的数据成员的指针实例化],并且给定类类型的内存表示形式是编译器特定的(例如,填充)。但是,从cppreference / Template parameters and template arguments,fasics,这是格式正确的:

模板非类型参数

实例化具有非类型模板参数的模板时,以下限制适用:

[..]

[直到C ++ 17]对于指向成员的指针,参数必须是指向以&Class::Member表示的成员的指针,或者是计算为空指针或std::nullptr_t值的常量表达式。

[..]

[自C ++ 17起>>]唯一的例外是引用或指针类型的非类型模板参数[自C ++ 20开始添加:并且引用或指针类型的非静态数据成员在类类型及其子对象的非类型模板参数(因为C ++ 20)]不能引用/是的地址

  • 子对象(包括非静态类成员,基础子对象或数组元素);
  • 一个临时对象(包括在引用初始化期间创建的对象;];>
  • 字符串文字;
  • typeid的结果;
  • 或预定义变量__func__
  • 这是如何工作的?是否要求编译器(通过直接或间接(例如,上述标准要求))自行解决此问题,只在成员之间存储(编译时)地址偏移量,而不是实际地址?

即/例如,是指向counter中数据成员非类型模板参数Foo::increaseCounter()的编译时间指针(针对两个特定的指向数据成员实例的指针中的每一个),仅是任何给定实例的编译时间地址偏移量Foo的值,该地址以后将成为Foo的每个实例的完全解析的地址,即使尚未分配的地址(例如f的块范围内的callMeWhenever())也是如此?

请考虑以下代码段:#include #include struct Foo {Foo():foo_(0U),bar_(0U){} void gainFoo(){gainCounter(); } ...

c++ templates pointer-to-member
1个回答
3
投票

是否需要编译器(通过直接或间接,例如上述标准要求)自行解决此问题,仅在成员之间存储(编译时)地址偏移量,而不是实际地址?

© www.soinside.com 2019 - 2024. All rights reserved.