代码之家  ›  专栏  ›  技术社区  ›  YSC

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

  •  9
  • YSC  · 技术社区  · 7 年前

    在下面的片段中,我对 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;
    }
    

    demo 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;
    }
    

    demo

    我觉得答案将包含诸如“推断上下文”之类的表达。。。但我真的无法确定这一行为标准的确切部分。

    有没有语言律师在这件事上给我启发?


    笔记:
    1) 但如果我尝试并有效地 呼叫 Wrapper<T>::f() const .
    2) 我用 -std=c++17 但这不是C++17特有的,因此没有特定的标记。
    3) 在里面 this answer ,@Baum mit Augen引号 [N4140, 14.7.1(2)] :

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

    但这里是编译代码段(#2) void f() const { _data.f(); } 尽管其 “专业化是 从不 在要求成员定义存在的上下文中引用“ .

    1 回复  |  直到 7 年前
        1
  •  4
  •   Jodocus    7 年前

    代码段#2为 格式错误 .

    如中所述 this answer ,的模板定义 Wrapper::f 只要有效的专门化可以 生成 .

    §17.7/8 [temp.res] 国家:

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

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

    在这两段代码中, Wrapper<void>::f 正在实例化,因为中的规则 §17.7.1/2 [temp.inst] :

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

    (强调由我完成)

    但现在§17.7/8开始生效:如果没有实例化 不可能有生成的专门化 其模板定义为 包装器::f 是有效的(对于代码段#2来说就是这种情况,对于每个生成的专门化 Wrapper<T>::f non-const 呼叫内部a const 将执行成员上的功能),程序格式错误,并发出诊断。

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

    但是,对于代码段#1,您可以为 Data 哪里 Data::f 常量 (说 Data<void>::f ). 因此, 生成 专业化 包装器::f 是可能的,即。 包装(<);无效(>::f . 综上所述,代码段#1格式正确,代码段#2无效;所有编译器都以符合标准的方式工作。