变长模板别名作为模板参数(第二部分)

13
这是对另一个问题的跟进。它涉及相同的问题(我希望如此),但使用完全不同的示例来说明。原因是在先前的示例中,只有实验性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...>

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

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


无论您使用的是哪个clang版本,clang 3.5仍然会显示“0, 0”(关于第一个测试)。 - Stephan Dollberg
有关这个问题的任何消息吗?我也有完全相同的问题/疑问。 - unkulunkulu
2
这是一个标准的开放问题:http://www.open-std.org/JTC1/SC22/WG21/docs/cwg_active.html#1430 - unkulunkulu
2个回答

2

这并没有回答上面的代码是否有效的问题,但是这是我在询问问题后不久进行实验后发现的一个非常好的解决方法,我认为分享出来会很有用。

所需的全部定义如下:

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...> 替换它即可。我无法解释原因,但目前为止它已经适用于所有情况。

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

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

by

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> 的实现中充满了此类代码。在某些情况下,这些代码块被定义为宏。有了这种解决方案,模板就能够胜任工作,不需要使用宏。


0

我认为这种情况已经相当标准化了;C++11 14.3.3/1 表示:

模板参数的模板实参必须是类模板或别名模板的名称,表示为 id-expression。


谢谢。我在之前的问题答案中看到了同样的引用,但由于我对标准语言不熟悉,它对我来说看起来很模糊。所以基本上你的意思是问题出在实现上,我应该应用任何解决方法,直到问题得到解决,这段代码才能正常工作。对吗?如果是这样,我也可以报告一个错误吗? - iavr

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接