模板参数中的访问控制

18

这个答案的启发,声称可以颠覆访问控制系统,我写了以下最小版本的黑客攻击。

template<typename T>
inline T memptr{};

template<auto Val>
struct setptr
{
    struct setter { setter() { memptr<decltype(Val)> = Val; } };
    static setter s;
};

template<auto Val>
typename setptr<Val>::setter setptr<Val>::s{};

然后将其用作


class S
{
    int i;
};

template struct setptr<&S::i>;

auto no_privacy(S& s)
{
    return s.*memptr<int S::*>;
}

为什么template struct setptr<&S::i>;不违反访问控制?
是因为[class.access],其中明确指出:

无论名称是从声明还是表达式中引用,访问控制都会统一应用于所有名称。

具体来说,它不包括实例化吗?如果是这样,那为什么不包括实例化? 勘误:显式实例化也被归类为声明。

请参见http://www.gotw.ca/gotw/076.htm。 - Jesper Juhl
我们实际上在代码库中仅在一个地方使用这个“hack”,以便能够调用我们确实需要调用的库中的私有成员函数;-) 但是,我怀疑它在测试框架中也可能很有用,因为您可能希望测试受保护和私有成员而无需注入友元和其他内容。 - Jesper Juhl
如果你从答案中跟随几个链接,你会到达这个有注释的版本,在那里详细说明了显式实例化是唯一可以忽略访问权限形成成员函数/字段指针的地方。在你链接的问题的答案评论中,甚至还有更多关于这个库的解释。 - Max Langhof
@MaxLanghof 我确实看到过那些内容,但是找不到他们声明的来源。 - Passer By
@PasserBy: Clang链接错误没有错误 - Jarod42
显示剩余3条评论
1个回答

11

来自[temp.spec]/6的说明(强调我的):

通常的访问检查规则不适用于显式实例化或显式特化声明中的名称,除了出现在函数体、默认参数、基础子句、成员说明、枚举器列表、静态数据成员或变量模板初始化程序中的名称。 [ 注意: 特别地,在函数声明符中使用的模板参数和名称(包括参数类型、返回类型和异常规范)可能是私有类型或对象,通常无法访问。 — 结尾注意  ]

因此,你看到的这种技术滥用了这个规则,这个规则主要是为了允许类的实现者使用私有类型或其他私有实体对模板(如特性)进行专门化。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接