虚拟模板参数中的SFINAE和std :: enable_if以基于两个条件选择结构

问题描述 投票:0回答:1

假设我们希望part调用模板化结构part<U,D>,其中D的类型为U

template<typename U, U D>
struct part{};

我们有两种情况:

  1. [Uunsigned类型,并且D%(sizeof(U)*8)==0
  2. [Uunsigned类型,并且D%(sizeof(U)*8)!=0

确定大小写的区别让我们拥有两个成员:valuedivisible。基本情况不满足条件,并且以valuedivisible均为false实例化struct。如果满足条件1),我应该具有value=true, divisible= false,如果满足条件2),我应该具有value=true, divisible= true。展望未来也可以说,如果满足1)或2),则我在结构中也有一个type

我设置了一个辅助结构choice以在std::enable_if中使用,并通过虚拟模板参数选择适当的大小写。代码如下:

// to be used in dummy template arguments
template<size_t D>
struct choice{};

// Base case
template<typename U, U D, typename E1 = choice<0>, typename E2 = choice<0> >
struct part{
  static constexpr bool value     = false;
  static constexpr bool divisible = false;
};

// Specialisation 1  <U, D, choice<1>, choice<0> >
template<typename U, U D>
struct part<U,D,std::enable_if_t<std::is_unsigned<U>::value, choice<1> >, std::enable_if_t<!(D%(sizeof(U)*8)), choice<0> > >{
  static constexpr bool value     = true;
  static constexpr bool divisible = false;
  typedef U type;
};

// Specialisation 2  <U, D, choice<1>, choice<1> >
template<typename U, U D>
struct part<U,D,std::enable_if_t<std::is_unsigned<U>::value, choice<1> >, std::enable_if_t<(D%(sizeof(U)*8)), choice<1> > >{
  static constexpr bool value     = true;
  static constexpr bool divisible = true;
  typedef U type;
};

此编译。大。让我们回顾一下使用简单打印所做的事情:

  std::cout << "Should get 0,0 "<< std::endl;
  std::cout << part<int,5>::value << std::endl;
  std::cout << part<int,5>::divisible << std::endl;

  std::cout << "Should get 1,0 "<< std::endl;
  std::cout << part<uint16_t,5>::value << std::endl;
  std::cout << part<uint16_t,5>::divisible << std::endl;

  std::cout << "Should get 1,1 "<< std::endl;    
  std::cout << part<uint16_t,16>::value << std::endl;
  std::cout << part<uint16_t,16>::divisible << std::endl;

  std::cout << "Should get 1,0 "<< std::endl;    
  std::cout << part<uint16_t,30>::value << std::endl;
  std::cout << part<uint16_t,30>::divisible << std::endl;  

  std::cout << "Should get 1,1 "<< std::endl;      
  std::cout << part<uint16_t,32>::value << std::endl;
  std::cout << part<uint16_t,32>::divisible << std::endl;


// RESULTS ON TERMINAL ARE:
//
// Should get 0,0 
// 0
// 0
// Should get 1,0 
// 0
// 0
// Should get 1,1 
// 0
// 0
// Should get 1,0 
// 0
// 0
// Should get 1,1 
// 0
// 0

所以根本不是我想要的。好的,可以尝试:

std::cout << std::is_same<part<uint16_t,5>::type, uint16_t> << std::endl;

// COMPILATION ERROR:
src/tests/test1.cpp(55): error: class "part<uint16_t={unsigned short}, (uint16_t={unsigned short})5U, choice<0UL>, choice<0UL>>" has no member "type"
    std::cout << std::is_same<part<uint16_t,5>::type, uint16_t> << std::endl;

我要去哪里错了?代码经过编译,因此SFINAE正在解决专业化问题。但它接缝总是选择基本情况。为什么?

c++ templates sfinae enable-if
1个回答
3
投票

这不是SFINAE技巧的工作原理。您需要为std::enable_if_t提供与定义为默认参数的第二个参数相同的类型,这样,如果std::enable_it_t不会导致替换失败,则部分专业化将与未明确指定默认参数的模板专业化匹配。

[因此,您想在各处使用choice<0>而不是choice<1>,并且因为您在所有地方都使用void,因此您实际上可以在任何地方使用choice<...>而不是void,并且使用std::enable_if_t,您也可以将第二个参数放在[ C0],因为默认情况下是这样。

然后,您不需要两个启动器参数,只需将条件与逻辑运算连接起来:

// Base case
template<typename U, U D, typename = void>
struct part{
  static constexpr bool value     = false;
  static constexpr bool divisible = false;
};

// Specialisation 1  <U, D>
template<typename U, U D>
struct part<U,D,std::enable_if_t<std::is_unsigned_v<U> && !(D%(sizeof(U)*8))>{
  static constexpr bool value     = true;
  static constexpr bool divisible = false;
  typedef U type;
};

// Specialisation 2
template<typename U, U D>
struct part<U,D,std::enable_if_t<std::is_unsigned_v<U> && (D%(sizeof(U)*8))>{
  static constexpr bool value     = true;
  static constexpr bool divisible = true;
  typedef U type;
};

还要注意,根据您显示的内容,您不需要最后的专业化:

// Base case
template<typename U, U D, typename = void>
struct part{
  static constexpr bool value     = false;
  static constexpr bool divisible = false;
};

// Specialisation 1  <U, D>
template<typename U, U D>
struct part<U,D,std::enable_if_t<std::is_unsigned_v<U>> {
  static constexpr bool value     = true;
  static constexpr bool divisible = (D%(sizeof(U)*8) == 0);
  typedef U type;
};

SFINAE方法仅对于std::is_unsigned_v是必需的,因为如果(D%(sizeof(U)*8) == 0)的类型不是无符号整数,则表达式D可能格式错误。

此外,由于使用C ++ 17,您可以这样做:

template<typename U, U D>
struct part{
  static constexpr bool value     = std::is_unsigned_v<U>;
  static constexpr bool divisible = []{
      if constexpr(value)
          return D%(sizeof(U)*8) == 0;
      else
          return false;
  }();
};

为了完全避免部分专业化,即使U可能是D%(sizeof(U)*8)格式不正确的类型。

如果您不打算支持格式不正确的D%(sizeof(U)*8),请使用:

template<typename U, U D>
struct part{
  static constexpr bool value     = std::is_unsigned_v<U>;
  static constexpr bool divisible = value && (D%(sizeof(U)*8) == 0);
};
© www.soinside.com 2019 - 2024. All rights reserved.