使用enable_if分别定义和声明模板成员函数,该模板的模板参数还包含constexpr成员函数

时间:2018-08-08 23:20:34

标签: c++ c++11 sfinae

以下内容在CentOS 7的g ++ 8.1.0下无法编译:

嘿.h

#pragma once
#include <iostream>
#include <type_traits>

class Valid {};
class Invalid {};

struct Hey
{
  template<typename T>
  static constexpr bool is_valid() { return std::is_same_v<T, Valid>; }

  template<typename T, std::enable_if_t<is_valid<T>()>* = nullptr>
    void howdy() const;
};

template<typename T, std::enable_if_t<Hey::is_valid<T>()>*>
  void Hey::howdy() const
{
  std::cout << "Howdy" << std::endl;
}

编译器输出:

In file included from hey.cpp:1:
hey.h:18:8: error: no declaration matches ‘void Hey::howdy() const’
   void Hey::howdy() const
        ^~~
hey.h:14:10: note: candidate is: ‘template<class T, std::enable_if_t<is_valid<T>()>* <anonymous> > void Hey::howdy() const’
     void howdy() const;
          ^~~~~
hey.h:8:8: note: ‘struct Hey’ defined here
 struct Hey
        ^~~

令人惊讶的是,我要做的都是正确编译并获得所需的行为,就是在Hey中添加typedef:

hey.h(已修复,跳过了无聊的前几行)

struct Hey
{
  template<typename T>
  static constexpr bool is_valid() { return std::is_same_v<T, Valid>; }

  template<typename T>
  using EnableType = std::enable_if_t<is_valid<T>()>;

  template<typename T, EnableType<T>* = nullptr>
    void howdy() const;
};

template<typename T, Hey::EnableType<T>*>
  void Hey::howdy() const
{
  std::cout << "Howdy" << std::endl;
}

hey.cpp

#include "hey.h"

int main(int, char**)
{
  Hey hey;
  hey.howdy<Valid>();

  // Adding this line breaks the build, as it should:
  // hey.howdy<Invalid>();

  return 0;
}

经过多次调整,我将编译器错误的情况缩小为以下事实:1)is_valid()Hey的成员,而2)howdy()在{{1}内部声明}的身体,但在外部定义。如果删除Hey,并使using成为is_valid()之外的独立功能,则编译不会有问题。如果删除Hey并在类定义中定义using,那么编译也不会出现问题。但是,如果在类定义之外定义了howdy(),则在类定义内部声明了howdy(),并且没有出现is_valid()时,编译器将失败。这是正确的行为吗?我在查看编译器错误吗?

1 个答案:

答案 0 :(得分:2)

模板声明中表达式的匹配基于equivalence,这是基于一定义规则的概念。为了使两个表达式等效,它们必须至少是逐个令牌相同,而模板参数的重命名是相同的。

表达式is_valid<T>()Hey::is_valid<T>()不相等(第二个有两个标记,第一个没有两个标记),因此不需要编译器匹配它们。

Hey::EnableType<T>是一种类型,不受严格的表达式等价规则约束。