Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/162.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 与SFINAE结合使用时静态过载_C++_Sfinae - Fatal编程技术网

C++ 与SFINAE结合使用时静态过载

C++ 与SFINAE结合使用时静态过载,c++,sfinae,C++,Sfinae,我尝试在Visual Studio 2013中编译以下程序,得到了C2686:无法重载静态和非静态成员函数错误 #include <iostream> #include <type_traits> struct foo { template<bool P> static std::enable_if_t<P> bar() { std::cerr << "P is true\n"; } template&l

我尝试在Visual Studio 2013中编译以下程序,得到了
C2686:无法重载静态和非静态成员函数
错误

#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>();
}

在我看来,Visual Studio 2013在这两个案例中有一个是错误的,但我希望语言律师能给出明确的答案。

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

1并非所有函数声明都可以重载。这里指定了那些不能重载的。A. 如果程序在同一范围内包含两个这样的不可重载声明,则该程序是格式错误的。[注:此 限制适用于范围内的显式声明,以及此类声明和所做声明之间的显式声明 通过使用声明(7.3.3)。它不适用于因名称而制造的函数集 查找(例如,由于使用指令)或重载解析(例如,针对操作员功能)。-结束注释]

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

  • 不能重载仅在返回类型上不同的函数声明
  • 具有相同名称和名称的成员函数声明 如果参数类型列表中的任何一个是
    静态
    成员函数声明,则不能重载该列表(9.4)。同样,成员函数 具有相同名称的模板声明,相同 参数类型列表,如果同一模板参数列表中的任何一个是
    静态
    成员函数模板,则不能重载它们 宣言。[……]
  • [……]
两个
bar()
声明具有相同的名称、相同的参数类型列表和相同的模板参数列表,其中至少有一个是
静态的
,因此不能重载。

Background 我认为VisualStudio在这种情况下是不正确的

[1] 编译函数调用的状态分为两个步骤:名称查找,然后是重载解析(如果需要)(我们将在本例中看到这是不必要的)

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

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

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

例子 让我们手工编译这个例子

  • 编译器需要解析的函数调用

    f、 bar()

  • 首先,进行名称查找

  • 2.1。依赖于参数的查找运行。由于没有参数,此步骤不会减少候选列表

    2.2。模板参数推断运行

    2.2.1。模板参数替换运行。这将P值替换为每个enable_if_t表达式。如果\u t的谓词表达式(此处P)为false时生成替换失败,则启用\u。因此,P导致替换失败的函数将从候选列表中删除。在模板参数替换之后,此代码只能生成1个候选函数,因为enable_if_t表达式是互斥的

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

    工具书类
    • [1]
    • [2]
    • [3] http://en.cppreference.com/w/cpp/language/sfinae

    你确定Visual C++ 2013有<代码> STD::EnabLeIft

    <代码>吗?尝试将其写成

    typename std::enable_if

    ::type

    。我记得2013有一些C++11支持(对库的支持非常广泛),但没有C++14。哦,当一个人想到这些年的时候,这是理所当然的!它在gcc和clang@Cheers中编译成功是的,VC2013支持
    std::enable_if_t
    。通过删除第一次重载声明中的
    静态
    ,我上面的程序可以正确编译。@CraigM.Brandenburg:谢谢!今天也学到了一些新东西我编辑了我的问题,将
    静态
    案例与重载返回类型进行对比,在没有SFINAE的情况下,标准也禁止重载返回类型,但与
    静态
    案例不同,使用SFINAE时编译为OK。由于这种差异,我觉得你引用的标准中的段落不足以解释发生了什么。此外,它没有解释为什么,正如@Stephen在上面的评论中指出的那样,gcc和clang支持
    static
    案例。@CraigM.Brandenburg重载返回类型对于函数模板来说是好的,因为bullet 2.1(我刚刚添加到答案中)只禁止具有不同返回类型的函数,而不是函数模板(其签名包括返回类型-请参见[defns.signature.temp]和[defns.signature.member.temp])。至于GCC和Clang的行为,要么是错误,要么是故意不合规。Bullet 2.2明确禁止此处讨论的重载。感谢您澄清您的答案。此处的关键问题是SFINAE是否排除了两个函数定义在同一重载集中。如果是,则您引用的重载规则可能不适用因此,问题是:SFINAE如何与重载规则相关联?@CraigM.Brandenburg它们是一些正交的东西。[over.load]中的规则不适用于重载集的构建;它们使任何“在同一范围内包含两个这样的非重载声明”的程序都格式错误,而不管为任何给定调用构造的重载集是否会导致实际的歧义。有趣。因此禁止使用重载
    #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>();
    }