简体   繁体   English

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

[英]Well-formed program containing an ill-formed template member function?

In the snippet below, I'm puzzled about why the definition of Wrapper::f() const does not make my program ill-formed 1 although it calls a non-const member function of a non mutable member variable: 在下面的片段中,我很困惑为什么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;
}

demo 2 演示 2

On the other hand, if Data is a non template class 3 , a diagnostic is issued by my compiler: 另一方面,如果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;
}

demo 演示

I feel like the answer will contain expressions such as "deduced context" ... but I really cannot pin down the exact part of the standard scecifying this behaviour. 我觉得答案将包含诸如“演绎的上下文”之类的表达...但我真的无法确定标准的确切部分,以此行为。

Is there a language lawyer to enlighten me on the matter? 是否有语言律师在此事上启发我?


Notes: 笔记:
1) But I get an error if I try and effectively call Wrapper<T>::f() const . 1)但是如果我尝试有效地调用 Wrapper<T>::f() const我会收到错误。
2) I've compiled with -std=c++17 but this is not specific to C++17, hence no specific tag. 2)我用-std=c++17编译,但这不是特定于C ++ 17,因此没有特定的标记。
3) In this answer , @Baum mit Augen quotes [N4140, 14.7.1(2)] : 3)这个答案中 ,@ Baum mit Augen引用[N4140, 14.7.1(2)]

the specialization of the member is implicitly instantiated when the specialization is referenced in a context that requires the member definition to exist 当在需要成员定义存在的上下文中引用特化时,隐式实例化成员的特化

but here in the compiling snippet (#2) void f() const { _data.f(); } 但是在编译片段(#2)中void f() const { _data.f(); } void f() const { _data.f(); } fails although its "specialization is never referenced in a context that requires the member definition to exist" . void f() const { _data.f(); }失败,虽然它的“专业化是从来没有在任何需要成员定义存在的上下文中引用”。

Snippet #2 is ill-formed . 片段#2格式不正确

As already stated in this answer , the template definition of Wrapper::f is well-formed (thus no diagonstics are issued) as long as a valid specialization can be generated . 正如在这个答案中已经说明的那样,只要可以生成有效的专业化, Wrapper::f的模板定义就是格式良好的(因此不会发出任何描述)。

§17.7/8 [temp.res] states: §17.7/ 8 [temp.res]指出:

Knowing which names are type names allows the syntax of every template to be checked. 知道哪些名称是类型名称允许检查每个模板的语法。 The program is ill-formed, no diagnostic required, if: 如果出现以下情况,该计划格式错误,无需诊断:

  • no valid specialization can be generated for a template or a substatement of a constexpr if statement within a template and the template is not instantiated, or [...] 没有为模板或模板中的constexpr if语句的子语句生成有效的专业化,并且模板未实例化,或者[...]

In neither of the two code snippets, Wrapper<void>::f is getting instantiated, because of the rules in §17.7.1/2 [temp.inst] : 在两个代码片段中, Wrapper<void>::f都没有实例化,因为§17.7.1/ 2 [temp.inst]中的规则:

The implicit instantiation of a class template specialization causes the implicit instantiation of the declarations , but not of the definitions, [...]. 类模板特化的隐式实例化会导致声明的隐式实例化,而不是定义的实例化,[...]。

(emphasizing done by me) (强调由我完成)

But now §17.7/8 kicks in: if there is no instantiation and there can be no generated specialization for which the template definition of Wrapper::f is valid (which is the case for snippet #2, as for every generated specialization Wrapper<T>::f , a non-const call inside a const function on a member is would be performed), the program is ill-formed and diagnostics are issued. 但是现在§17.7/ 8开始了:如果没有实例化,并且没有生成的特殊化Wrapper::f的模板定义是有效的(对于片段#2就是这种情况,对于每个生成的特化Wrapper<T>::f ,将执行成员上的const函数内的non-const调用),程序格式错误并发出诊断。

But because the diagnostics are not mandatory (see §17.7/8 above), the GCC can deny snippet #2 while both VS and clang compile the same code flawlessly. 但由于诊断不是强制性的(参见上面的§17.7/ 8),GCC可以拒绝代码片段#2,而VSclang都可以完美地编译相同的代码。

For snippet #1 however you could provide a user-defined specialization for Data where Data::f is const (say Data<void>::f ). 对于代码片段#1,您可以为Data提供用户定义的特殊化,其中Data::fconst (比如Data<void>::f )。 Therefore, a valid, generated specialization of Wrapper::f is possible, ie Wrapper<void>::f . 因此, Wrapper::f的有效生成特化是可能的,即Wrapper<void>::f So in conclusion, snippet #1 is well-formed and snippet #2 is invalid; 总而言之,代码片段#1格式正确,代码片段#2无效; all compilers work in a standard-conforming manner. 所有编译器都以符合标准的方式工作。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM