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

YSC*_*YSC 9 c++ templates language-lawyer

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

// 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;
}
Run Code Online (Sandbox Code Playgroud)

demo2

另一方面,如果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;
}
Run Code Online (Sandbox Code Playgroud)

demo

我觉得答案将包含诸如"演绎的上下文"之类的表达......但我真的无法确定标准的确切部分,以表示这种行为.

是否有语言律师在此事上启发我?


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

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

但是这里的编译片段(#2)void f() const { _data.f(); }失败了,尽管它的"专门化从未在需要成员定义存在的上下文中引用".

Jod*_*cus 4

代码片段 #2 格式不正确

\n\n

正如这个答案Wrapper::f中已经指出的,只要可以生成有效的专业化, 的模板定义就是格式良好的(因此不会发出诊断) 。

\n\n

\xc2\xa717.7/8 [temp.res]状态:

\n\n
\n

知道哪些名称是类型名称可以检查每个模板的语法。如果出现以下情况,则程序格式错误,无需诊断:

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

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

\n\n
\n

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

\n
\n\n

(强调是我做的)

\n\n

但现在 \xc2\xa717.7/8 开始工作:如果没有实例化,并且不存在模板定义Wrapper::f有效的生成的专业化(这是片段 #2 的情况,对于每个生成的专业化Wrapper<T>::f,将在成员的函数non-const内部const执行调用),程序格式错误并发出诊断信息。

\n\n

但由于诊断不是强制性的(请参阅上面的 \xc2\xa717.7/8),GCC 可以拒绝代码段 #2,而VSclang都可以完美地编译相同的代码。

\n\n

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

\n