SFINAE 模板专用化优先级


#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专业。第二个模板旨在成为容器的通用专用化,没有更具体的特征。

#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

现在我们必须证明 #1 中的 #2 正在工作。如果是这样,我们已经证明#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应该没有问题。好吧,它的非推导上下文,但为什么它是一个问题?我唯一能想到的是,非推导上下文与部分排序相结合有一些特殊规则......

相关内容

  • 没有找到相关文章

最新更新