#include <iostream>
#include <array>
#include <vector>
template <typename T, typename SFINAE=void>
struct trait;
template <typename T>
struct trait<T, decltype(
std::declval<const T&>().begin(),
std::declval<const T&>().end(),
void()
)> {
static const char* name() { return "Container"; }
};
template <typename T, std::size_t N>
struct trait<std::array<T,N>> {
static const char* name() { return "std::array"; }
};
int main(int argc, char* argv[]) {
std::cout << trait<std::vector<int>>::name() << std::endl;
std::cout << trait<std::array<int,2>>::name() << std::endl;
}
我期望第三个模板比第二个模板更专业,但是我得到了模棱两可的模板实例化。
有没有办法使第三个模板更专业?明确检查第二个模板中的T
是否为std::array
对我来说不起作用。我正在写一个库,希望用户能够定义他们自己的trait
专业化。第二个模板旨在成为没有更具体特征的容器的通用专业化。
答案 0 :(得分:8)
#include <iostream>
#include <array>
#include <vector>
template <typename T, typename SFINAE=void>
struct trait;
template <typename T>
struct trait<T, std::void_t<decltype(std::declval<T>().begin()),
decltype(std::declval<T>().end())>> {
static const char* name() { return "Container"; }
};
template <typename T, std::size_t N>
struct trait<std::array<T,N>,void> {
static const char* name() { return "std::array"; }
};
int main(int argc, char* argv[]) {
std::cout << trait<std::vector<int>>::name() << std::endl;
std::cout << trait<std::array<int,2>>::name() << std::endl;
}
编辑
首先,对于以下内容,我们无法保证其确实是猜测,而不是证明。也许其他人可以更正,扩展复制粘贴或其他内容。
但是,看到这个问题后,我的第一个猜测是使用std::void_t
。我敢肯定,我以前见过类似的东西,但是,也不能保证。
为了证明可以使用std::void_t
,我们必须证明“一种模板专业化比另一种更为具体”。我们通过检查偏序来做到这一点。
我将用以下内容模仿上面的内容,该内容要简短一些。
template <typename T, typename SFINAE=void>
struct trait;
//#1
template <typename T>struct trait<T, std::void_t<decltype(std::declval<T>().begin())>>
{
static const char* name() { return "Container"; }
};
//#2
template <typename T>struct trait<std::vector<T>,void> {
static const char* name() { return "std::vector"; }
};
我不会解释如何完成部分排序,这将花费很长时间。转换为函数等之后,最终得到的内容类似于以下内容。
//#2 from #1: f(trait<std::vector<T>,void>) from f(trait<__ANY_TYPE__, std::void_t<decltype(std::declval<__ANY_TYPE__>().begin())>)
//P=trait<std::vector<T>,void>
//A=trait<__ANY_TYPE__, std::void_t<decltype(std::declval<__ANY_TYPE__>().begin())>>
//P1=std::vector<T>
//A1=__ANY_TYPE__
//P2=void
//A2=std::void_t<decltype(std::declval<__ANY_TYPE__>().begin())>
//==> T=? --> fail, #2 from #1 is not working
现在我们必须证明#2中的#1正在工作。如果是这样,我们已经证明#2更专业。
//#1 from #2: f(trait<T, std::void_t<decltype(std::declval<T>().begin())>>) from f(trait<std::vector<__ANY_TYPE__>,void>)
//P=trait<T, std::void_t<decltype(std::declval<T>().begin())>>
//A=trait<std::vector<__ANY_TYPE__>,void>
//P1=T
//A1=std::vector<__ANY_TYPE__>
//P2=std::void_t<decltype(std::declval<T>().begin())> //(*)
//A2=void
//==> T=std::vector<__ANY_TYPE__> ok #1 from #2 works
基本上,这是我的草图,无需检查标准或其他任何内容。我很确定您可以在标准的无尽行中找到它。
如果您注意的话,您会注意到(*)。如果要使用decltype(...),此行基本上是唯一重要的一行。我的猜测是使用decltype(...)会导致右侧的非推导上下文
这可能不允许使用P1 / A1推论中的T。但是,是的,这基本上就是为什么我没有首先提供有效的std::void_t
解决方案答案的原因。
最后,由于类型名部分,我认为类型{的另一种std::void_t
定义是非推导上下文,就像decltype(...)一样。
编辑
只需添加最后几行。原则上,decltype sfinae应该没有问题。确定其非推论上下文,但是为什么会出现问题呢?我唯一能想到的是,非推导上下文具有一些与部分排序结合的特殊规则...