与SFINAE

时间:2015-06-26 00:15:22

标签: c++ sfinae

我尝试在Visual Studio 2013中编译以下程序并得到C2686: cannot overload static and non-static member functions错误。

#include <iostream>
#include <type_traits>

struct foo
{
  template<bool P>
  static std::enable_if_t<P> bar()
  {
    std::cerr << "P is true\n";
  }

  template<bool P>
  std::enable_if_t<!P> bar()
  {
    std::cerr << "P is false\n";
  }
};

int main()
{
  foo f;
  f.bar<true>();
}

我熟悉这个编译器错误 - 请参阅this StackOverflow answer,但是很惊讶地看到错误与SFINAE结合,编译器总是会丢弃过载集中的两个重载中的一个。

Visual Studio 2013是否正确遵循此处的标准,或者是否可以与SFINAE一起使静态重载?

编辑:上面的对比示例,返回类型重载

如果没有SFINAE,您不能在static上重载,并且您也不能在返回类型上重载。但是,Visual Studio 2013支持与SFINAE一起在返回类型上重载。

以下程序与上面的程序相同但删除static并更改第二个foo::bar声明的返回类型,正确编译。

#include <iostream>
#include <type_traits>

struct foo
{
  template<bool P>
  std::enable_if_t<P> bar()
  {
    std::cerr << "P is true\n";
  }

  template<bool P>
  std::enable_if_t<!P, int> bar()
  {
    std::cerr << "P is false\n";
    return 42;
  }
};

int main()
{
  foo f;
  f.bar<true>();
}

在我看来,Visual Studio 2013会让这两个案例中的一个出错,但我希望语言律师可以提供明确的答案。

2 个答案:

答案 0 :(得分:5)

令人惊讶的是,MSVC是正确的。 (我知道,震惊。)[over.load] / p1-2:

  

1并非所有函数声明都可以重载。这里指定了那些不能重载的东西。一个   如果程序在同一范围内包含两个这样的不可重载的声明,则程序格式不正确。 [注意:这个   限制适用于范围中的显式声明,以及此类声明和声明之间的声明   通过 using-declaration (7.3.3)。它不适用于由于名称而制作的函数集   查找(例如,由于 using-directives )或重载决策(例如,对于操作员功能)。 - 结束记录]

     

2某些函数声明不能​​重载:

     
      
  • 只能在返回类型上有所不同的函数声明不能​​重载。
  •   
  • 具有相同名称和相同的成员函数声明   如果 parameter-type-list 中的任何一个是static成员函数声明(9.4),则不能重载它。同样,成员函数   模板声明具有相同的名称,相同    parameter-type-list ,如果其中任何一个是static成员函数模板,则不能重载相同的模板参数列表   宣言。 [...]
  •   
  • [...]
  •   

两个bar()声明具有相同的名称,相同的 parameter-type-list 和相同的模板参数列表,其中至少有一个是static,因此不能超载。

答案 1 :(得分:1)

背景

我认为在这种情况下Visual Studio是不正确的。

[1]状态编译函数调用分两步进行,名称查找,然后在必要时进行重载解析(在这种情况下我们将不会看到)。

名称查找生成候选函数列表。名称查找由两个步骤组成,即依赖于参数的查找,然后是模板参数推导。

如果名称查找生成多个可能的函数调用,则会发生重载解析以确定要调用的正确函数。

SFINAE是一种元编程技术,用于在模板参数推导的子步骤中操纵候选函数集[2]。 SFINAE在模板参数替换期间引发替换错误,这阻止了将所述函数添加到候选集[3]。

实施例

让我们手动编译这个例子。

  1. 编译器需要解析

    的函数调用

    f.bar();

  2. 首先,名称查找发生

  3. 2.1。参数依赖查找运行。由于没有参数,这一步骤不会减少候选人名单。

    2.2。模板参数推断运行。

    2.2.1。模板参数替换运行。这将P值替换为每个enable_if_t&lt;&gt;表达。 enable_if_t&LT;&GT;当它的谓词表达式(此处为P)为假时,会生成替换失败。因此,从候选列表中移除P引起替换失败的函数。在模板参数替换之后,该代码只能产生1个候选函数,因为enable_if_t&lt;&gt;表达是互斥的。

    结论

    在模板参数替换步骤完成之前,Visual Studio似乎正在检查重载规则。如果它已经运行模板参数替换,那么由于候选列表包含单个函数,因此永远不会发生重载解析。

    参考