为什么这种替换失败再次产生错误?



我之前问了一个问题,为什么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;工作,而当我用双精度实例化它时,它无法编译,这让我认为它们是两个不同的问题。

编辑:我应该提到,这无法使用Visual Studio Community 2019 16.9.3进行编译。

// Failed to specialize alias template

首先,您的代码中没有别名模板。 你只是在关心bIsIntegralTypestd::is_integral_v<value_t_arg>完全相同的东西,一旦发生underlyingtype的实例化,它就会被固定(falsetrue)。

因此,这两个专业

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;
};

是一回事,因此叮当说

Class template partial specialization 'IsSpecialType<T>' cannot be redeclared

这与你传递给underlyingtypevalue_t_arg无关。

当删除两个相同的专用化中的任何一个时,代码在underlyingtype<int> g1;方面是可以的,但是在尝试实例化underlyingtype<double>时它仍然无效,因为在这种情况下value_t_arg被"阻止"到double,这使得bIsIntegralType只是一个false编译时值,这反过来意味着你正在传递一个永远和永远的falsestd::enable_if_v

换句话说,当你要求underlyingtype<double>时,编译器开始用value_t_arg = doubleunderlyingtype实例化类;此时编译器甚至没有看过IsSpecialType,但它知道bIsIntegralType == false,这使得IsSpecialType的特化代码在上一个问题中无效。


(¹) 别名模板是模板化类型别名,

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是一个模板,是(非模板)类类型的成员underlyingtype<double>此模板具有单个参数 T。
  • underlyingtype<double>::IsSpecialType<char>是上述模板的实例化。

现在,在实例化模板时,参数将替换为实际参数。未能执行此类替换不是错误。在underlyingtype<double>::IsSpecialType的情况下,参数为T。然而std::enable_if_t<std::is_integral_v<value_t_arg>>>不依赖于T,所以不会发生替换。

最新更新