使用 enable_if 的模板成员函数的单独定义和声明,其模板参数还包括一个 constexpr 成员函数
Separate definition and declaration of template member function using enable_if whose template parameter also includes a constexpr member function
以下在 CentOS 7 上的 g++ 8.1.0 下无法编译:
hey.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()
在 [= 中声明16=]'s body but defined outside.如果删除 using
并使 is_valid()
成为 Hey
之外的独立函数,则编译没有问题。如果删除 using
并在 class 定义中定义 howdy()
,编译也没有问题。但是当 howdy()
在 class 定义之外定义时, is_valid()
在 class 定义内部声明,并且 using
不存在,编译器失败。这是正确的行为吗?我在看编译器错误吗?
模板声明中表达式的匹配是基于equivalence,一个基于单一定义规则的概念。对于被认为是等价的两个表达式,它们必须至少是逐个令牌相同的模版参数重命名模数。
表达式is_valid<T>()
和Hey::is_valid<T>()
不等价(第二个有两个标记,第一个没有),因此编译器不需要匹配它们。
Hey::EnableType<T>
是一种类型,不受表达式严格等价规则的约束。
以下在 CentOS 7 上的 g++ 8.1.0 下无法编译:
hey.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()
在 [= 中声明16=]'s body but defined outside.如果删除 using
并使 is_valid()
成为 Hey
之外的独立函数,则编译没有问题。如果删除 using
并在 class 定义中定义 howdy()
,编译也没有问题。但是当 howdy()
在 class 定义之外定义时, is_valid()
在 class 定义内部声明,并且 using
不存在,编译器失败。这是正确的行为吗?我在看编译器错误吗?
模板声明中表达式的匹配是基于equivalence,一个基于单一定义规则的概念。对于被认为是等价的两个表达式,它们必须至少是逐个令牌相同的模版参数重命名模数。
表达式is_valid<T>()
和Hey::is_valid<T>()
不等价(第二个有两个标记,第一个没有),因此编译器不需要匹配它们。
Hey::EnableType<T>
是一种类型,不受表达式严格等价规则的约束。