这是另一个问题的后续。它指的是同一个问题(我希望),但使用了一个完全不同的例子来说明它。原因是在前面的示例中,只有实验性 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);
^
(行号可能会有所不同)。所以,一切都失败了,以不同的方式。
现在,这里有一个解决方法。元函数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...>
到目前为止的意见说这是有效的,但现在似乎三个编译器不同意。所以问题又来了:它有效吗?
对我来说这很重要,因为我有几十个现有代码文件,假设这是有效的,现在我无论如何都需要重新设计(因为这些编译器存在实际问题),但确切的重新设计将取决于答案。