为什么这种替换失败会再次产生错误?
Why does this substitution failure create an error, again?
我刚才问了一个问题,为什么 std::enable_if<false>
不能在 SFINAE 上下文中使用,如:
template <typename T, typename DEFAULTVOID = void>
struct TemplatedStruct {};
template <typename T>
struct TemplatedStruct<T, std::enable_if_t<false>> {}; // enable_if expression
// isn't dependent on template type, is always false and so is an error
但是在下面的示例中它依赖于模板参数,但这也会产生错误:
#include <type_traits>
template <typename value_t_arg>
struct underlyingtype
{
static inline constexpr bool bIsIntegralType =
std::is_integral_v<value_t_arg>;
template <typename T, typename DEFAULTVOID = void>
struct IsSpecialType {
static inline constexpr bool bIsSpecialType = false;
};
template <typename T>
struct IsSpecialType<T, std::enable_if_t<bIsIntegralType>> {
static inline constexpr bool bIsSpecialType = true;
};
// This also creates an error, this is essentially the same as above
template <typename T>
struct IsSpecialType<T, std::enable_if_t<std::is_integral_v<value_t_arg>>> {
static inline constexpr bool bIsSpecialType = true;
};
};
int main()
{
underlyingtype<int> g1; // Works
underlyingtype<double> g2; // std::enable_if_t<false, void>:
// Failed to specialize alias template
}
在使用 std::enable_if_t<false>
的第一种情况下,无论我实例化什么,它都无法编译。然而,在另一种情况下,underlyingtype<int> g1;
可以工作,而当我用 double 实例化它时,它无法编译,这让我认为它们是两个不同的问题。
编辑:我应该提一下,这无法使用 Visual Studio Community 2019 16.9.3.
进行编译
// Failed to specialize alias template
首先,您的代码中没有别名模板。¹您只是将 bIsIntegralType
定义为与 std::is_integral_v<value_t_arg>
完全相同的东西,它是固定的(到 false
或 true
) 一旦 underlyingtype
的实例化发生。
因此,两个专业
template <typename T>
struct IsSpecialType<T, std::enable_if_t<bIsIntegralType>> {
static inline constexpr bool bIsSpecialType = true;
};
// This also creates an error, this is essentially the same as above
template <typename T>
struct IsSpecialType<T, std::enable_if_t<std::is_integral_v<value_t_arg>>> {
static inline constexpr bool bIsSpecialType = true;
};
是同一件事,因此 clang 说
Class template partial specialization 'IsSpecialType<T>' cannot be redeclared
这与 value_t_arg
您传递给 underlyingtype
的内容无关。
当删除两个相同的特化中的任何一个时,关于 underlyingtype<int> g1;
的代码是可以的,但在尝试实例化 underlyingtype<double>
时它仍然无效,因为 value_t_arg
被“阻止” " 到 double
在这种情况下,这使得 bIsIntegralType
只是一个 false
编译时值,这反过来意味着你正在传递一个永远永远的 false
到 std::enable_if_v
.
换句话说,当您请求 underlyingtype<double>
时,编译器开始用 value_t_arg = double
实例化 class underlyingtype
;此时编译器甚至没有查看 IsSpecialType
,但它知道 bIsIntegralType == false
,这使得 IsSpecialType
的代码根据 的特化无效。
(¹) alias template 是模板化的类型别名,
template <typename T>
using new_name = old_name<T>;
而在您的代码中根本没有 using
,因此不可能有类型别名,更不用说别名模板了。
根据这个问题和上一个问题,您似乎正在尝试进入 SFINAE 和模板元编程。如果我可以给你一个建议,学习它的一个好方法是阅读和理解 Boost.Hana 库是如何工作的。那里有 很多 的 TMP 和 SFINAE,但是代码的质量很高(恕我直言),代码本身也有很好的文档记录,而且,因此,可以理解(显然这需要时间)。
考虑这一行:
std::cout << underlyingtype<double>::IsSpecialType<char>::bIsSpecialType << "\n";
我们应该如何解读它?
underlyingtype
是模板。
underlyingtype<double>
不是模板,是类型,underlyingtype
. 的具体实例化
underlyingtype<double>::IsSpecialType
是一个模板,一个(非模板)class类型的成员underlyingtype<double>
这个模板只有一个参数T.
underlyingtype<double>::IsSpecialType<char>
是前面模板的实例化。
现在,在实例化模板时,其 参数将替换为实际参数。未能执行此类替换不是错误。在 underlyingtype<double>::IsSpecialType
的情况下,参数是 T
。但是 std::enable_if_t<std::is_integral_v<value_t_arg>>>
不依赖于 T
,因此不会发生替换。
我刚才问了一个问题,为什么 std::enable_if<false>
不能在 SFINAE 上下文中使用,如:
template <typename T, typename DEFAULTVOID = void>
struct TemplatedStruct {};
template <typename T>
struct TemplatedStruct<T, std::enable_if_t<false>> {}; // enable_if expression
// isn't dependent on template type, is always false and so is an error
但是在下面的示例中它依赖于模板参数,但这也会产生错误:
#include <type_traits>
template <typename value_t_arg>
struct underlyingtype
{
static inline constexpr bool bIsIntegralType =
std::is_integral_v<value_t_arg>;
template <typename T, typename DEFAULTVOID = void>
struct IsSpecialType {
static inline constexpr bool bIsSpecialType = false;
};
template <typename T>
struct IsSpecialType<T, std::enable_if_t<bIsIntegralType>> {
static inline constexpr bool bIsSpecialType = true;
};
// This also creates an error, this is essentially the same as above
template <typename T>
struct IsSpecialType<T, std::enable_if_t<std::is_integral_v<value_t_arg>>> {
static inline constexpr bool bIsSpecialType = true;
};
};
int main()
{
underlyingtype<int> g1; // Works
underlyingtype<double> g2; // std::enable_if_t<false, void>:
// Failed to specialize alias template
}
在使用 std::enable_if_t<false>
的第一种情况下,无论我实例化什么,它都无法编译。然而,在另一种情况下,underlyingtype<int> g1;
可以工作,而当我用 double 实例化它时,它无法编译,这让我认为它们是两个不同的问题。
编辑:我应该提一下,这无法使用 Visual Studio Community 2019 16.9.3.
进行编译
// Failed to specialize alias template
首先,您的代码中没有别名模板。¹您只是将 bIsIntegralType
定义为与 std::is_integral_v<value_t_arg>
完全相同的东西,它是固定的(到 false
或 true
) 一旦 underlyingtype
的实例化发生。
因此,两个专业
template <typename T>
struct IsSpecialType<T, std::enable_if_t<bIsIntegralType>> {
static inline constexpr bool bIsSpecialType = true;
};
// This also creates an error, this is essentially the same as above
template <typename T>
struct IsSpecialType<T, std::enable_if_t<std::is_integral_v<value_t_arg>>> {
static inline constexpr bool bIsSpecialType = true;
};
是同一件事,因此 clang 说
Class template partial specialization 'IsSpecialType<T>' cannot be redeclared
这与 value_t_arg
您传递给 underlyingtype
的内容无关。
当删除两个相同的特化中的任何一个时,关于 underlyingtype<int> g1;
的代码是可以的,但在尝试实例化 underlyingtype<double>
时它仍然无效,因为 value_t_arg
被“阻止” " 到 double
在这种情况下,这使得 bIsIntegralType
只是一个 false
编译时值,这反过来意味着你正在传递一个永远永远的 false
到 std::enable_if_v
.
换句话说,当您请求 underlyingtype<double>
时,编译器开始用 value_t_arg = double
实例化 class underlyingtype
;此时编译器甚至没有查看 IsSpecialType
,但它知道 bIsIntegralType == false
,这使得 IsSpecialType
的代码根据
(¹) alias template 是模板化的类型别名,
template <typename T>
using new_name = old_name<T>;
而在您的代码中根本没有 using
,因此不可能有类型别名,更不用说别名模板了。
根据这个问题和上一个问题,您似乎正在尝试进入 SFINAE 和模板元编程。如果我可以给你一个建议,学习它的一个好方法是阅读和理解 Boost.Hana 库是如何工作的。那里有 很多 的 TMP 和 SFINAE,但是代码的质量很高(恕我直言),代码本身也有很好的文档记录,而且,因此,可以理解(显然这需要时间)。
考虑这一行:
std::cout << underlyingtype<double>::IsSpecialType<char>::bIsSpecialType << "\n";
我们应该如何解读它?
underlyingtype
是模板。underlyingtype<double>
不是模板,是类型,underlyingtype
. 的具体实例化
underlyingtype<double>::IsSpecialType
是一个模板,一个(非模板)class类型的成员underlyingtype<double>
这个模板只有一个参数T.underlyingtype<double>::IsSpecialType<char>
是前面模板的实例化。
现在,在实例化模板时,其 参数将替换为实际参数。未能执行此类替换不是错误。在 underlyingtype<double>::IsSpecialType
的情况下,参数是 T
。但是 std::enable_if_t<std::is_integral_v<value_t_arg>>>
不依赖于 T
,因此不会发生替换。