使用可变参数对元组内容进行部分特化

发布于 2024-11-25 07:54:02 字数 2035 浏览 1 评论 0 原文

目前,我正在尝试让一些代码对不同类型做出不同的反应。这不是确切的代码,但它传达了信息。

template<class A, class B>
struct alpha {
  enum { value = 0 };
};

template<class T, class... Args>
struct alpha<std::tuple<Args...>, T> {
  enum { value = 1 };
};

// This gets ignored
template<class T, class... Args>
struct alpha<std::tuple<Args..., std::vector<T> >, T> {
  enum { value = 2 };
};

// This gets ignored
template<class T, class... Args>
struct alpha<std::tuple<Args..., T>, T> {
  enum { value = 3 };
};

template<class T, class... Args>
struct alpha<T, std::tuple<Args...> > {
  enum { value = 4 };
};

template<class... LArgs, class... RArgs>
struct alpha<std::tuple<LArgs...>, std::tuple<RArgs...> > {
  enum { value = 5 };
};

int main(int argc, char* argv[]) {
  std::cout << alpha<std::tuple<int, double>, double>::value << std::endl; // prints 1
  return 0;
}

我已经尝试了比此代码显示的更多内容,但到目前为止没有任何效果,并且我遇到了非命名空间范围中显式专业化的问题。作为参考,我正在开发 gcc 4.6(oneiric 服务器附带的版本),我相信它具有完整的可变参数模板支持。我不在乎如果实现能够检测参数包的最后一个参数和其他类型,它会变得多么难看。有什么建议吗?

编辑: 我想分享我根据答案使用的解决方案(这是一个例子)。

template<typename T> struct tuple_last;

template<typename T, typename U, typename... Args>
struct tuple_last<std::tuple<T,U,Args...>> {
  typedef typename tuple_last<std::tuple<U,Args...>>::type type;
};

template<typename T>
struct tuple_last<std::tuple<T>> {
  typedef T type;
};

namespace details {
// default case:
template<class T, class U>
struct alpha_impl {
enum { value = 1 };
};

template<class T>
struct alpha_impl<T, T> {
enum { value = 101 };
};

template<class T>
struct alpha_impl<T, std::vector<T>> {
enum { value = 102 };
};

// and so on.
}

template<class T, class... Args>
struct alpha<std::tuple<Args...>, T>
  : details::alpha_impl<T, tuple_last<std::tuple<Args...>>;

Currently, I'm trying to get some code to react differently to different types. This isn't the exact code, but it gets the message across.

template<class A, class B>
struct alpha {
  enum { value = 0 };
};

template<class T, class... Args>
struct alpha<std::tuple<Args...>, T> {
  enum { value = 1 };
};

// This gets ignored
template<class T, class... Args>
struct alpha<std::tuple<Args..., std::vector<T> >, T> {
  enum { value = 2 };
};

// This gets ignored
template<class T, class... Args>
struct alpha<std::tuple<Args..., T>, T> {
  enum { value = 3 };
};

template<class T, class... Args>
struct alpha<T, std::tuple<Args...> > {
  enum { value = 4 };
};

template<class... LArgs, class... RArgs>
struct alpha<std::tuple<LArgs...>, std::tuple<RArgs...> > {
  enum { value = 5 };
};

int main(int argc, char* argv[]) {
  std::cout << alpha<std::tuple<int, double>, double>::value << std::endl; // prints 1
  return 0;
}

I've tried more than this code shows, but nothing works so far and I ran across a problem with explicit specialization in a non-namespace scope. For reference, I'm working on gcc 4.6 (the one that comes with oneiric server), which I believe has complete variadic template support. I don't care how ugly it gets if the implementation works to detect the last argument of the parameter pack and the other types as well. Any suggestions?

EDIT:
I wanted to share the solution I used based on the answers (this is an example).

template<typename T> struct tuple_last;

template<typename T, typename U, typename... Args>
struct tuple_last<std::tuple<T,U,Args...>> {
  typedef typename tuple_last<std::tuple<U,Args...>>::type type;
};

template<typename T>
struct tuple_last<std::tuple<T>> {
  typedef T type;
};

namespace details {
// default case:
template<class T, class U>
struct alpha_impl {
enum { value = 1 };
};

template<class T>
struct alpha_impl<T, T> {
enum { value = 101 };
};

template<class T>
struct alpha_impl<T, std::vector<T>> {
enum { value = 102 };
};

// and so on.
}

template<class T, class... Args>
struct alpha<std::tuple<Args...>, T>
  : details::alpha_impl<T, tuple_last<std::tuple<Args...>>;

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

浮华 2024-12-02 07:54:02

如果您使用 clang 进行编译,它会报告 (2) 和 (3) 不可用。您希望选择的 (3) 的警告如下:

警告:类模板部分特化包含无法推导的模板参数;这种部分特化永远不会被使用

struct alpha, T> {
       ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

注意:不可推导的模板参数“Args

模板
                           ^

为什么 Args 不可推导? C++0x FDIS 在 §14.8.2.5/9 中规定:

如果[根据模板参数指定的类型]的模板参数列表包含不是最后一个模板参数的包扩展,则整个模板参数列表是非推导上下文。

在您的专业化中,类型 std::tuple 是根据模板参数 ArgsT< 指定的类型/代码>。它包含一个包扩展 (Args...),但该包扩展不是最后一个模板参数(T 是最后一个模板参数)。因此,tuple 的整个模板参数列表( 的整体)是一个非推导的上下文。

std::tuple 的参数列表是模板特化的参数列表中唯一出现 Args 的位置;因为它不能从那里推导,所以它根本不能推导,并且专业化永远不会被使用。

Matthieu M. 在他的答案中提供了一个巧妙的解决方法

If you compile using clang, it helpfully reports that (2) and (3) are unusable. The warning for (3), which you expect to be selected, is as follows:

warning: class template partial specialization contains a template parameter that can not be deduced; this partial specialization will never be used

struct alpha<std::tuple<Args..., T>, T> {
       ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

note: non-deducible template parameter 'Args'

template<class T, class... Args>
                           ^

Why is Args not deducible? The C++0x FDIS states at §14.8.2.5/9:

If the template argument list of [a type that is specified in terms of template parameters] contains a pack expansion that is not the last template argument, the entire template argument list is a non-deduced context.

In your specialization, the type std::tuple<Args..., T> is a type that is specified in terms of template parameters Args and T. It contains a pack expansion (Args...), but that pack expansion is not the last template argument (T is the last template argument). Thus, the entire template argument list of the tuple (the entirety of <Args..., T>) is a non-deduced context.

The argument list of the std::tuple is the only place in the template specialization's argument list that Args appears; since it is not deducible from there, it is not deducible at all and the specialization will never be used.

Matthieu M. provides a clever workaround in his answer.

鱼忆七猫命九 2024-12-02 07:54:02

@James 提供了原因,现在让我们尝试寻找替代方案。

我建议使用另一个间接级别。

1.获取最后一个参数

template <typename T> struct Last;

template <typename T, typename U, typename... Args>
struct Last<std::tuple<T,U,Args...>>
{
  typedef typename Last<std::tuple<U,Args...>>::type type;
};

template <typename T>
struct Last<std::tuple<T>>
{
  typedef T type;
};

2.引入专门的帮手

template <typename T, typename U>
struct alpha_tuple
{
  enum { value = 1 };
};

template <typename T>
struct alpha_tuple<T,T>
{
  enum { value = 3 };
};

template <typename T>
struct alpha_tuple<std::vector<T>,T>
{
  enum { value = 2; }
};

3.连接起来

template <typename T>
struct alpha<std::tuple<>, T>
{
  enum { value = 1 };
};

template <typename T, typename U, typename Args...>
struct alpha<std::tuple<U, Args...>, T>
{
  typedef typename Last<std::tuple<U, Args...>>::type LastType;
  enum { value = alpha_tuple<LastType,T>::value };
};

请注意,空元组没有最后一种类型,因此我必须在单独的专业化中处理它们。

@James provided the why, now let's try to find an alternative.

I would suggest using another level of indirection.

1. Getting the last argument

template <typename T> struct Last;

template <typename T, typename U, typename... Args>
struct Last<std::tuple<T,U,Args...>>
{
  typedef typename Last<std::tuple<U,Args...>>::type type;
};

template <typename T>
struct Last<std::tuple<T>>
{
  typedef T type;
};

2. Introducing a specialized helper

template <typename T, typename U>
struct alpha_tuple
{
  enum { value = 1 };
};

template <typename T>
struct alpha_tuple<T,T>
{
  enum { value = 3 };
};

template <typename T>
struct alpha_tuple<std::vector<T>,T>
{
  enum { value = 2; }
};

3. Hooking it up

template <typename T>
struct alpha<std::tuple<>, T>
{
  enum { value = 1 };
};

template <typename T, typename U, typename Args...>
struct alpha<std::tuple<U, Args...>, T>
{
  typedef typename Last<std::tuple<U, Args...>>::type LastType;
  enum { value = alpha_tuple<LastType,T>::value };
};

Note that there is no last type for empty tuples, so I had to deal with them in a separate specialization.

怼怹恏 2024-12-02 07:54:02

如果你想知道一个元组是否作为特定的最后一个成员,这里有一个类型特征:

#include <type_traits>
#include <tuple>

template <typename ...Args> struct back;
template <typename T, typename ...Args> struct back<T, Args...>
  { typedef typename back<Args...>::type type; };
template <typename T> struct back<T>
  { typedef T type; };


template <typename...> struct tuple_has_last : public std::false_type {};
template <typename T, typename... Args> struct tuple_has_last<T, std::tuple<Args...>>
{
  static const bool value = std::is_same<typename back<Args...>::type, T>::value;
};

编辑:哦,我没有看到Matthieu已经写了完全相同的东西。没关系。

If you like to find out whether a tuple as a specific last member, here's a type trait for that:

#include <type_traits>
#include <tuple>

template <typename ...Args> struct back;
template <typename T, typename ...Args> struct back<T, Args...>
  { typedef typename back<Args...>::type type; };
template <typename T> struct back<T>
  { typedef T type; };


template <typename...> struct tuple_has_last : public std::false_type {};
template <typename T, typename... Args> struct tuple_has_last<T, std::tuple<Args...>>
{
  static const bool value = std::is_same<typename back<Args...>::type, T>::value;
};

Edit: Oh, I didn't see that Matthieu had already written the exact same thing. Never mind.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文