具有ref限定符的模板方法的重载解析

时间:2019-02-04 17:47:16

标签: c++ visual-c++ language-lawyer

我正在使用特殊类型开发具有编译时访问功能的容器。我还希望有一个使用数字的访问函数,以便为所有元素实现操作。因此,我有这样的东西:

struct S
{
    template<int I> int& f();
    template<class Q> int& f();
};

我想禁止对临时对象的访问,所以我为类型访问添加了重载:

struct S
{
    template<int I> int& f();
    template<class Q> int& f() &;
    template<class Q> int& f() && = delete;
};

但是随后我遇到了msvc编译器的问题:

<source>(4): error C2560: 'int &Test::f(void) &': cannot overload a member function with ref-qualifier with a member function without ref-qualifier

但是gcc和clang都接受它。谁是对的?

https://godbolt.org/z/4bmA2-

2 个答案:

答案 0 :(得分:2)

MSVC在这里是错误的。

相关规则为[over.load]/2.3

  

具有相同名称和相同参数类型列表的成员函数声明,以及具有相同名称,相同参数类型列表,和相同模板参数列表的成员函数模板声明如果其中任何一个(但不是全部)具有ref限定符([dcl.fct]),则不能重载。

此处功能模板具有不同的模板参数(int Iclass Q),因此此规则不适用,也没有其他规则可以阻止它们重载。

答案 1 :(得分:1)

此处的MSVC是正确的。您不能同时具有ref限定符和非ref限定符重载。

为什么不呢?因为它们是模棱两可的。 ref限定成员函数具有一个限定符,该限定符指定this指向的对象是左值(&)还是右值(&&)。同时,非ref合格的成员函数接受 b 左值右值。这会产生歧义,编译器会将其视为错误。

请注意,此代码的template方面是红色鲱鱼。使用简单的方法会遇到很多麻烦:

struct S
{
    int& f();       // non-ref-qualifier
    int& f() &;     // ref-qualifier (this must be lvalue)
    int& f() &&;    // ref-qualifier (this must be rvalue)
};

甚至只是:

struct S
{
    int& f();       // non-ref-qualifier
    int& f() &;     // ref-qualifier (this must be lvalue)
};

C ++ 14语言标准为over.match.funcs中的ref限定成员函数定义了候选解析的语义。具体来说,第13.3.1节[4]:

  

对于非静态成员函数,隐式对象参数的类型为

     
      
  • “对 cv X的左值引用”,用于声明没有 ref-qualifier 或&&ref-qualifier
  • 的函数   
  • “对 cv X的右值引用”,用于使用&& ref-qualifier
  • 声明的函数   
     

其中X是该函数是成员的类,而 cv 是成员函数声明中的cv限定词。 [示例:对于const的{​​{1}}成员函数,假定额外参数的类型为“对class X的引用”。 — 结束示例]对于转换函数,出于定义隐式对象参数的类型的目的,该函数被视为隐式对象参数的类的成员。对于 using-declaration 引入派生类的非转换函数,出于定义隐式对象参数类型的目的,该函数被视为派生类的成员。对于静态成员函数,隐式对象参数被视为与任何对象匹配(因为如果选择了该函数,则该对象将被丢弃)。 [注意:没有为静态成员函数的隐式对象参数建立实际类型,也不会尝试确定该参数的转换顺序([over.match.best])。 — 尾注]

和§13.4.1[5](强调):

  

在重载解析期间,隐含对象参数与​​其他参数没有区别。但是,隐式对象参数保留其标识,因为无法应用用户定义的转换来实现与之匹配的类型。 对于声明为没有 ref-qualifier 的非静态成员函数,适用附加规则:

     
      
  • 即使隐式对象参数不是const限定的,只要在所有其他方面参数都可以转换为隐式对象参数的类型,就可以将右值绑定到该参数 。 [注意:这样的参数是右值的事实并不影响隐式转换序列的排名。 — 尾注]
  •   

MSVC针对此ref限定符过载歧义有两个警告:

  
      
  • 编译器错误 C2559
      ' identifier ':如果没有带有ref-qualifier的成员函数的ref-qualifier,则无法重载成员函数

  •   
  • 编译器错误 C2560
      ' identifier ':无法使用带有ref-qualifier的成员函数重载带有ref-qualifier的成员函数

  •   

要禁止在临时对象(rvalues)上调用成员函数,只需执行以下操作即可:

const X

或者,使用模板:

struct S
{
    int& f() &;            // ref-qualifier (this must be lvalue)
    int& f() && = delete;  // ref-qualifier (this must be rvalue)
};