包含格式错误的模板成员函数的格式正确的程序?

Well-formed program containing an ill-formed template member function?

本文关键字:格式 函数 程序 错误 包含 成员      更新时间:2023-10-16

在下面的代码片段中,我很困惑为什么Wrapper::f() const的定义不会使我的程序格式错误1,尽管它调用了不可变成员变量的非常量成员函数:

// well-formed program (???)
// build with: g++ -std=c++17 -Wall -Wextra -Werror -pedantic
template<class T> struct Data { void f() {} };
template<class T> struct Wrapper
{
Data<T> _data;
void f() const { _data.f(); } // _data.f(): non-const!
};
int main()
{
Wrapper<void> w; // no error in instantiation point?
(void) w;
}

演示2

另一方面,如果Data是非模板类3,则我的编译器会发出诊断:

// ill-formed program (as expected)
// build with: g++ -std=c++17 -Wall -Wextra -Werror -pedantic
struct Data { void f() {} };
template<class T> struct Wrapper
{
Data _data;
void f() const { _data.f(); } //error: no matching function for call to 'Data::f() const'
};
int main()
{
Wrapper<void> w;
(void) w;
}

演示

我觉得答案将包含诸如"推断上下文"之类的表达方式......但我真的无法确定标准中这种行为的确切部分。

有没有语言律师来启发我这件事?


注意:
1)但是如果我尝试有效地调用Wrapper<T>::f() const,我会出错。
2)我已经用-std=c++17编译了,但这并不特定于C++17,因此没有特定的标签。
3)在这个答案中,@Baum mit Augen引用了[N4140, 14.7.1(2)]

当在需要成员定义的存在的上下文中引用专用化时,将隐式实例化成员的专用化

但是在编译代码段中,void f() const { _data.f(); }失败,尽管它的"在需要成员定义的上下文中从未引用过专用化"。

代码段 #2 格式不正确

如本答案中所述,只要可以生成有效的专用化,Wrapper::f的模板定义就是格式正确的(因此不会发出对角线)。

§17.7/8 [临时决议] 规定:

知道哪些名称是类型名称允许每个模板的语法 待检查。程序格式不正确,无需诊断,如果:

  • 无法为模板或模板中的 constexpr if 语句的子语句生成有效的专用化,并且 模板未实例化,或 [...]

在这两个代码片段中,由于 §17.7.1/2 [temp.inst] 中的规则,Wrapper<void>::f都没有被实例化:

类模板专用化的隐式实例化导致声明的隐式实例化,但不是 定义,[...]。

(强调由我完成)

但是现在 §17.7/8 开始了:如果没有实例化,并且不能生成Wrapper::f模板定义有效的专用化(代码段 #2 就是这种情况,就像每个生成的专用化Wrapper<T>::f一样,将在成员上的const函数内执行non-const调用), 程序格式不正确,已发出诊断程序。

但是由于诊断不是强制性的(参见上面的§17.7/8),GCC可以拒绝代码段#2,而VS和clang都可以完美地编译相同的代码。

但是,对于代码段 #1,您可以为Data::fconstData提供用户定义的专用化(例如Data<void>::f)。因此,有效的、生成的Wrapper::f专业化是可能的,即Wrapper<void>::f.因此,总而言之,代码段 #1 格式正确,代码段 #2 无效;所有编译器都以符合标准的方式工作。