假设我们希望part
调用模板化结构part<U,D>
,其中D
的类型为U
:
template<typename U, U D>
struct part{};
我们有两种情况:
U
是unsigned
类型,并且D%(sizeof(U)*8)==0
U
是unsigned
类型,并且D%(sizeof(U)*8)!=0
确定大小写的区别让我们拥有两个成员:value
和divisible
。基本情况不满足条件,并且以value
和divisible
均为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正在解决专业化问题。但它接缝总是选择基本情况。为什么?
这不是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);
};