为什么不允许非静态成员的地址作为模板非类型参数?

nav*_*nav 26 c++ templates language-lawyer

template <int * ip> struct test {};

struct q {
    static int a;
    int b;

    constexpr q(int b_) : b(b_) {}
};

int i;
constexpr q q0(2);

int main()
{
    constexpr test<&i> t1;      // Works fine
    constexpr test<&q::a> t2;   // Works 
    constexpr test<&q0.b> t3;   // Does not work; address of non-static member?

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

尽管&q0.b在编译期间已知模板参数,但上述代码中的t3声明仍然失败.一些谷歌搜索显示标准不允许这样做(第14.3.2节):

[注意:数组元素的地址和非静态类成员的名称或地址是不可接受的模板参数.

X <&m.m> x4; //错误:非静态元素的地址

那么,尽管全局变量的非静态成员的地址在编译时是唯一的以及已知的,但为什么这个标准明确禁止这一点呢?

T.C*_*.C. 15

首先,要使用指向子对象的指针/引用,您需要能够对它们进行修改.这是一项非常艰巨的任务.

第二,也许更重要的是,来自N4198:

保留常量表达式必须命名完整对象的限制,以避免指向子对象的指针出现锯齿问题:

struct A { int x, y; } a;
template<int*> struct Z;
using B = Z<&a.x + 1>;
using C = Z<&a.y>;
// Are B and C the same type?
Run Code Online (Sandbox Code Playgroud)

引用理查德史密斯,

答案"是"是有问题的,因为你可以用[ a.y] 的指针做一些事情,如果在超过[ a.x] 末尾的指针上执行则会有未定义的行为答案"否"是有问题的,因为它们(在典型的实现中)代表相同的地址.