SFINAE 模板专业化优先级

2024-03-26

#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 is an 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;
}

EDIT

首先,不能保证以下内容实际上更多的是猜测而不是证明。也许其他人可以更正、扩展复制粘贴或其他什么。

然而,看到问题后我的第一个猜测是使用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_ttypename ... 的定义我认为由于 typename 部分,非推导上下文也像 decltype(...) 一样。


EDIT

只是添加最后几行。原则上 decltype sfinae 应该没有问题。好吧,它的非推导上下文,但为什么这是一个问题呢?我唯一能想到的是,非推导上下文有一些与部分排序相结合的特殊规则......

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

SFINAE 模板专业化优先级 的相关文章

随机推荐