变量模板别名作为模板参数(第2部分)

这是另一个问题的后续。 它指的是同样的问题(我希望),但用一个完全不同的例子来说明它。 原因是在前面的例子中,只有实验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 。 克朗说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);
                                                   ^

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

现在,这是一个解决方法。 Metafunction 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...>

迄今为止的观点认为这是有效的,但现在看来有三位编者不同意。 所以问题又来了: 它有效吗?

对我而言,这很重要,因为我基于现有代码的数十个文件,假设这是有效的,现在我需要重新设计(因为这些编译器存在实际问题),但确切的重新设计将取决于答案。


这并没有回答上述代码是否有效的问题,但是我提出这个问题后很快就通过实验发现了一个非常漂亮的解决方法,我认为这对分享很有用。

所有需要的是以下定义:

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 <A...> subs <F, A...>是否有问题,都用subs <F, A...>替换它。 而已。 我无法解释为什么,但迄今为止它在所有情况下都有效。

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

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

通过

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

这只是一个变化,所有其余的代码保持不变。 您不需要重新定义或包装每个用作F模板元函数。 这里有一个实例。

如果F <A...> ...>确实有效并且编译器最终支持它,那么返回也很容易,因为更改很少。

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

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

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


我认为情况非常好, C ++ 11 14.3.3 / 1说:

模板模板参数的模板参数应该是类模板或别名模板的名称,表示为id-expression。

链接地址: http://www.djcxy.com/p/82541.html

上一篇: Variadic template aliases as template arguments (part 2)

下一篇: Inferring return type of templated member functions in CRTP