【问题标题】:Variadic template aliases as template arguments (part 2)可变模板别名作为模板参数(第 2 部分)
【发布时间】:2013-12-15 23:31:43
【问题描述】:

这是another question 的后续。它指的是同一个问题(我希望),但使用了一个完全不同的例子来说明它。原因是在前面的示例中,只有实验性 GCC 4.9 因编译器错误而失败。在此示例中,Clang 和 GCC 4.8.1 也以不同的方式失败:Clang 产生了意外结果,而 GCC 4.8.1 报告了不同的错误消息。

上一个问题的答案或多或少说代码是有效的,问题出在 GCC 的实验版本上。但这个结果让我更加怀疑。几个月来我一直被一些我怀疑相关(或相同)的问题所困扰,这是我第一次有一个小的具体例子来说明。

所以,这里有一些代码。首先,一些通用代码将 SFINAE 应用于由可变参数模板别名元函数 F 指定的任意测试:

#include <iostream>
using namespace std;

using _true  = integral_constant <bool, true>;
using _false = integral_constant <bool, false>;

template <typename T> using pass = _true;

template <template <typename...> class F>
struct test
{
    template <typename... A> static _false           _(...);
    template <typename... A> static pass <F <A...> > _(int);
};

template <template <typename...> class F, typename... A>
using sfinae = decltype(test <F>::template _<A...>(0));

第二个,具体的测试,检查给定的类是否定义了一个名为type的类型:

template <typename T> using type_of  = typename T::type;
template <typename T> using has_type = sfinae <type_of, T>;

最后,举个例子:

struct A { using type = double; };

int main()
{
    cout << has_type <int>() << ", ";
    cout << has_type <A>()   << endl;
}

预期的结果是0, 1。 Clang 说0, 0。 GCC 4.8.1 说

tst.cpp: In substitution of ‘template<class T> using type_of = typename T::type [with T = A ...]’:
tst.cpp:15:51: required from ‘struct test<type_of>’
tst.cpp:19:67: required by substitution of ‘template<template<class ...> class F, class ... A> using sfinae = decltype (test:: _<A ...>(0)) [with F = type_of; A = {T}]’
tst.cpp:24:58: required from here
tst.cpp:23:56: error: ‘A ...’ is not a class, struct, or union type
  template <typename T> using type_of = typename T::type; 
                                                        ^

GCC 4.9 说

tst.cpp:19:67:   required by substitution of ‘template<template<class ...> class F, class ... A> using sfinae = decltype (test:: _<A ...>(0)) [with F = type_of; A = {T}]’
tst.cpp:24:58:   required from here
tst.cpp:15:51: error: pack expansion argument for non-pack parameter ‘T’ of alias template ‘template<class T> using type_of = typename T::type’
  template <typename... A> static pass <F <A...> > _(int);
                                                   ^

(行号可能会有所不同)。所以,一切都以不同的方式失败了。

现在,这里有一个解决方法。元函数car 从给定包中选择第一个类型,然后将测试重新定义为type_of2,现在是可变参数:

template <typename... T> struct car_t;
template <typename... T> using  car = type_of <car_t <T...> >;

template <typename T, typename... Tn>
struct car_t <T, Tn...> { using type = T; };

template <typename... T> using type_of2  = typename car <T...>::type;
template <typename T>    using has_type2 = sfinae <type_of2, T>;

int main()
{
    cout << has_type2 <int>() << ", ";
    cout << has_type2 <A>()   << endl;
}

现在所有三个编译器都按预期显示0, 1。有趣的是,对于任何版本的 GCC,我们都必须删除 has_type(即使我们不使用它)并只留下 has_type2;否则我们会有类似的错误。

总结:我发现一个模板需要一个可变参数模板参数的问题

template <typename...> class F

我们实际输入表单的非可变模板别名

template <typename T> using alias = // ... anything including T or not

最后调用F,就好像它是可变参数一样:

F <A...>

到目前为止,意见认为这是有效的,但现在似乎三个编译器不同意。所以问题又来了:有效吗

对我来说这很重要,因为我假设这是有效的,我有几十个现有代码文件,现在我无论如何都需要重新设计(因为这些编译器存在实际问题),但确切的重新设计将取决于回答。

【问题讨论】:

标签: c++ templates variadic-templates template-meta-programming template-aliases


【解决方案1】:

这并不能回答上面的代码是否有效的问题,但这是我在提出问题后不久通过实验发现的一个非常好的解决方法,我认为分享有用。

只需要以下定义:

template <template <typename...> class F>
struct temp { };

template <typename... A, template <typename...> class F>
F <A...> subs_fun(temp <F>);

template <template <typename...> class F, typename... A>
using subs = decltype(subs_fun <A...>(temp <F>()));

然后,如果F &lt;A...&gt; 有问题,请将其替换为subs &lt;F, A...&gt;。而已。我无法解释原因,但到目前为止它在所有情况下都有效。

例如,在问题的 SFINAE 示例中,只需替换行

template <typename... A> static pass <F <A...> > _(int);

通过

template <typename... A> static pass <subs <F, A...> > _(int);

这只是一次更改,所有剩余代码保持不变。您不需要重新定义或包装每个用作F 的模板元函数。这是live example

如果F &lt;A...&gt; 确实有效并且编译器最终支持它,那么再次切换回来很容易,因为更改很少。

我觉得这很重要,因为它只允许在两行中指定 SFINAE 测试

template <typename T> using type_of  = typename T::type;
template <typename T> using has_type = sfinae <type_of, T>;

并且是完全通用的。通常,每个这样的测试至少需要 10 行代码,&lt;type_traits&gt; 的实现充满了这样的代码。在某些情况下,此类代码块被定义为宏。有了这个解决方案,模板就可以完成这项工作,并且不需要宏。

【讨论】:

    【解决方案2】:

    我认为情况非常标准化; C++11 14.3.3/1 说:

    模板template-parametertemplate-argument应该是类模板或别名模板的名称,表示为id-expression em>。

    【讨论】:

    • 谢谢。我在上一个问题的答案中看到了相同的引用,但由于我对标准的语言完全不熟悉,所以对我来说它看起来很模糊。所以基本上你所说的是问题出在实现上,我应该应用任何解决方法,直到问题得到解决,此时这段代码将起作用。正确的?在这种情况下,我可能还会报告错误?
    猜你喜欢
    • 1970-01-01
    • 2016-11-18
    • 1970-01-01
    • 1970-01-01
    • 2017-09-22
    相关资源
    最近更新 更多