SFINAE 模板专业化优先级 [英] SFINAE template specialization precedence

查看:46
本文介绍了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;
}

我期待第三个模板比第二个更专业,但我得到了一个模棱两可的模板实例.

I was expecting the third template to be more specialized than the second, but I got an ambiguous template instantiation.

有没有办法让第三个模板更加专业化?明确检查 T 是否是第二个模板中的 std::array 对我不起作用.我正在编写一个库,并希望用户能够定义他们自己的 trait 特化.第二个模板旨在成为没有更具体特征的容器的通用特化.

Is there a way to make the third template more specialized? Explicitly checking for whether T is an std::array in the second template won't work for me. I'm writing a library and would like users to be able to define their own specializations of the trait. The second template is intended to be a generic specialization for containers in absence of a more specific 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;
}

<小时>

编辑

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

First off, no guarantee for the following its really more a guess than a prove. Maybe someone else can correct, extend copy paste it or whatever.

然而,我看到问题后的第一个猜测是使用 std::void_t.我很确定我以前见过这样的事情,但也不能保证.为了证明可以使用 std::void_t,我们必须证明一个模板特化比另一个更具体".我们通过检查偏序来做到这一点.我将用以下更短的内容来模仿上述内容.

However my first guess after seeing the question was to use std::void_t. I am pretty sure I have seen something like this before, but yeah would also not guarantee it. In order to show that std::void_t can be used we have to show that "one template specialization is more specific than the other". And we do this by checking partial order. I'll mimic the above with the following which is a bit more short.

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"; }
};

我不会解释部分排序是如何完成的,这会花费太长时间.在转换为函数等之后......你最终会得到类似于以下内容.

I am not going to explain how partial ordering is done, would take too long. After transforming to functions etc... you end up with something similar to the following.

//#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 更专业.

Now we have to show that #1 from #2 is working. If so we have shown that #2 is more specialized.

//#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

这基本上是我的草图,没有检查标准或其他任何东西.我很确定你可以在无尽的标准行中找到它......

Thats basically my sketch without checking the standard or anything else. I am pretty sure you can find it somewhere in the endless lines of the standard...

如果您注意了,就会注意到 (*).如果您想使用 decltype(...),这一行基本上是唯一重要的行.我的猜测是使用 decltype(...) 会导致右侧的非推导上下文这可能不允许使用 P1/A1 扣除中的 T.但是,是的,这基本上就是为什么我没有首先对有效的 std::void_t 解决方案提供答案的原因.最后,带有 typename ... 的替代 std::void_t 定义是我认为非推导的上下文也像 decltype(...),由于 typename 部分.

If you paid attention, you'll have noticed the (*). This line is basically the only important one if you want to use decltype(...). My guess is that using decltype(...) is leading to non-deduced context for the right hand side which is maybe not allowing to use the T from the P1/A1 deduction. But yeah this is basically the reason why i did not included an answer first to the working std::void_t solution. Finally the alternative std::void_t definition with typename ... is I think non-deduced context too just like decltype(...), due to the typename part.

编辑

只是添加几行最后一行.原则上,decltype sfinae 不应该有问题.好的,它的非推导上下文,但为什么会出现问题?我唯一能想到的是,非推导的上下文结合偏序有一些特殊的规则......

Just to add a few final lines. In principle there should not be a problem with decltype sfinae. Ok its non-deduced context, but why is it a problem? The only thing I can think of, is that non-deduced context has some special rules in combination with partial ordering...

这篇关于SFINAE 模板专业化优先级的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆