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

iav*_*avr 13 c++ templates template-meta-programming variadic-templates template-aliases

这是另一个问题的后续行动.它指的是同样的问题(我希望),但使用一个完全不同的例子来说明它.原因是在前面的示例中,只有实验性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));
Run Code Online (Sandbox Code Playgroud)

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

template <typename T> using type_of  = typename T::type;
template <typename T> using has_type = sfinae <type_of, T>;
Run Code Online (Sandbox Code Playgroud)

最后一个例子:

struct A { using type = double; };

int main()
{
    cout << has_type <int>() << ", ";
    cout << has_type <A>()   << endl;
}
Run Code Online (Sandbox Code Playgroud)

预期的结果是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; 
                                                        ^
Run Code Online (Sandbox Code Playgroud)

和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);
                                                   ^
Run Code Online (Sandbox Code Playgroud)

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

现在,这是一个解决方法.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;
}
Run Code Online (Sandbox Code Playgroud)

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

总结:我看到一个模板的问题,期望表单的可变参数模板参数

template <typename...> class F
Run Code Online (Sandbox Code Playgroud)

我们实际上给出了表单的非变量模板别名作为输入

template <typename T> using alias = // ... anything including T or not
Run Code Online (Sandbox Code Playgroud)

最后调用F就好像它是可变的:

F <A...>
Run Code Online (Sandbox Code Playgroud)

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

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

Ker*_* SB 0

我认为这种情况已经非常标准化了;C++11 14.3.3/1 说:

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