我正在研究Stroustroup的书“C ++编程第4版”。而我正试图在矩阵设计上遵循他的例子。
他的矩阵类很大程度上依赖于模板,我会尽力把它们搞清楚。 这是此矩阵的辅助类之一
Matrix_slice是映射a的Matrix实现的一部分 下标到元素位置的集合。它使用了这个想法 广义切片(第40.5.6节):
template<size_t N>
struct Matrix_slice {
Matrix_slice() = default; // an empty matrix: no elements
Matrix_slice(size_t s, initializer_list<size_t> exts); // extents
Matrix_slice(size_t s, initializer_list<size_t> exts, initializer_list<siz e_t> strs);// extents and strides
template<typename... Dims> // N extents
Matrix_slice(Dims... dims);
template<typename... Dims,
typename = Enable_if<All(Convertible<Dims,size_t>()...)>>
size_t operator()(Dims... dims) const; // calculate index from a set of subscripts
size_t size; // total number of elements
size_t start; // star ting offset
array<size_t,N> extents; // number of elements in each dimension
array<size_t,N> strides; // offsets between elements in each dimension
};
I
以下是构建我问题主题的行:
template<typename... Dims,
typename = Enable_if<All(Convertible<Dims,size_t>()...)>>
size_t operator()(Dims... dims) const; // calculate index from a set of subscripts
在本书前面他描述了如何实现Enable_if和All():
template<bool B,typename T>
using Enable_if = typename std::enable_if<B, T>::type;
constexpr bool All(){
return true;
}
template<typename...Args>
constexpr bool All(bool b, Args... args)
{
return b && All(args...);
}
我有足够的信息来了解它们是如何工作的,通过查看他的Enable_if实现,我也可以推断出Convertible函数:
template<typename From,typename To>
bool Convertible(){
//I think that it looks like that, but I haven't found
//this one in the book, so I might be wrong
return std::is_convertible<From, To>::value;
}
所以,我可以介绍这个模板函数声明的构建块 但是当我试图理解它们如何工作时我很困惑。 我希望你能帮忙
template<typename... Dims,
//so here we accept the fact that we can have multiple arguments like (1,2,3,4)
typename = Enable_if<All(Convertible<Dims,size_t>()...)>>
//Evaluating and expanding from inside out my guess will be
//for example if Dims = 1,2,3,4,5
//Convertible<Dims,size_t>()... = Convertible<1,2,3,4,5,size_t>() =
//= Convertible<typeof(1),size_t>(),Convertible<typeof(2),size_t>(),Convertible<typeof(3),size_t>(),...
//= true,true,true,true,true
//All() is thus expanded to All(true,true,true,true,true)
//=true;
//Enable_if<true>
//here is point of confusion. Enable_if takes two tamplate arguments,
//Enable_if<bool B,typename T>
//but here it only takes bool
//typename = Enable_if(...) this one is also confusing
size_t operator()(Dims... dims) const; // calculate index from a set of subscripts
那么我们到底得到了什么? 这个结构
template<typename ...Dims,typename = Enable_if<true>>
size_t operator()(Dims... dims) const;
问题是:
更新 您可以查看我在此处引用的同一本书中的代码 The C++ Programming Language 4th edition第841页(矩阵设计)
答案 0 :(得分:4)
这是基本的SFINAE。例如,您可以阅读here。
对于答案,我在这里使用的是std::enable_if_t
,而不是书中给出的EnableIf
,但两者是相同的:
如@GuyGreer的回答所述,第二个模板参数默认为void
。
代码可以作为“普通”功能模板定义读取
template<typename ...Dims, typename some_unused_type = enable_if_t<true> >
size_t operator()(Dims... dims) const;
使用=
,参数some_unused_type
默认为右侧的类型。并且由于没有明确使用类型some_unused_type
,因此也不需要为其命名并将其留空。
这也是C ++中常用的函数参数方法。检查例如operator++(int)
- 一个不写operator++(int i)
或类似的东西。
一起发生的事情是SFINAE,它是替换失败不是错误的缩写。这里有两种情况:
首先,如果std::enable_if_t
的布尔参数为false
,则会得到
template<typename ...Dims, typename = /* not a type */>
size_t operator()(Dims ... dims) const;
由于typename =
的rhs上没有有效类型,因此类型扣除失败。但是,由于SFINAE,它不会导致编译时错误,而是从重载集中删除函数。
实践中的结果就好像没有定义函数一样。
其次,如果std::enable_if_t
的布尔参数为true
,则会得到
template<typename ...Dims, typename = void>
size_t operator()(Dims... dims) const;
现在typename = void
是一个有效的类型定义,因此无需删除该函数。因此可以正常使用。
应用于您的示例,
template<typename... Dims,
typename = Enable_if<All(Convertible<Dims,size_t>()...)>>
size_t operator()(Dims... dims) const;
以上意味着仅当All(Convertible<Dims,size_t>()...
为true
时此函数才存在。这基本上意味着函数参数应该都是整数索引(我个人而言,我会根据std::is_integral<T>
写出来。)
答案 1 :(得分:1)
尽管缺少constexpr
,std::enable_if
是一个带有两个参数的模板,但第二个默认为void
。在为此编写快速别名时保留该约定是有道理的。
因此,别名应定义为:
template <bool b, class T = void>
using Enable_if = typename std::enable_if<b, T>::type;
我不知道书中是否存在此默认参数,只是这样可以解决该问题。
一个类型的赋值被称为type alias,并且它在锡上执行的操作,当您引用别名时,您实际上指的是它的别名。在这种情况下,这意味着当您编写Enable_if<b>
时,编译器会轻松地将其扩展为typename std::enable_if<b, void>::type
,为您节省所有额外输入。
最终得到的是一个只有在传递给它的每个参数都可以转换为std::size_t
时才可调用的函数。如果不满足特定条件,这将允许忽略函数的重载,这是一种更强大的技术,而不仅仅是匹配类型以选择要调用的函数。 std::enable_if
的链接提供了有关您为什么要这样做的更多信息,但我警告初学者这个主题有点令人兴奋。