且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

变量模板别名作为模板参数

更新时间:2023-11-30 20:43:58

ISO C ++ 11 14.3.3 / 1:


模板模板参数的模板参数应为类模板或别名模板的名称


此外,我看不到可变参数模板参数的任何特殊例外。


另一方面,这样的模板别名在真实世界代码中导致严重的问题,我不能在这里重现:频繁的内部编译器错误为gcc,意外的行为clang(只在更先进的SFINAE测试)。


根本问题可以在其他地方。你应该尝试本地化的代码,这会导致内部编译器错误 - 只是删除不相关的部分逐个(或使用某种二进制搜索,即分而治之) - 并检查错误是否仍然在每个阶段。






对于GCC 4.9.0错误,尝试更改

  template< typename ... T> 
using map = F< T ...&gt ;;

  template< typename ... U> 
using map = F< U ...&gt ;;

也许这有助于了解GCC看到的内容。


First some code, then some context, then the question:

template <typename T> using id = T;

template <template <typename...> class F, typename... T>
using apply1 = F <T...>;

template <template <typename...> class F>
struct apply2
{
    template <typename... T>
    using map = F <T...>;
};

// ...

cout << apply1 <id, int>() << endl;
cout << apply2 <id>::map <int>() << endl;

Both clang 3.3 and gcc 4.8.1 compile this without error, applying the identity metafunction to int, so both expressions evaluate to a default int (zero).

The fact that id is a template <typename> while apply1, apply2 expect a template <typename...> did concern me in the first place. However, it is quite convenient that this example works because otherwise metafunctions like apply1, apply2 would have to be so much more involved.

On the other hand, such template aliases cause serious problems in real-world code that I cannot reproduce here: frequent internal compiler errors for gcc, and less frequent unexpected behavior for clang (only in more advanced SFINAE tests).

After months of trial and error, I now install and try the code on the (experimental) gcc 4.9.0, and here comes the error:

test.cpp: In instantiation of ‘struct apply2<id>’:
test.cpp:17:22: error: pack expansion argument for non-pack parameter ‘T’ of alias template ‘template<class T> using id = T’
  using map = F <T...>; 
                      ^

Ok, so it seems this code was not valid all this time, but gcc crashed in various ways instead of reporting the error. Interestingly, while apply1, apply2 appear to be equivalent, the error is only reported for apply2 (which is much more useful in practice). As for clang, I really cannot say.

In practice, it seems I have no other way than to go along with gcc 4.9.0 and correct the code, even though it will become much more complex.

In theory, I would like to know what the standard says: is this code valid? If not, is the use of apply1 invalid as well? or only apply2?

EDIT

Just to clarify that all problems I've had so far refer to template aliases, not template structs. For instance, consider the following modification:

template <typename T> struct id1 { using type = T; };

// ...

cout << typename apply1 <id1, int>::type() << endl;
cout << typename apply2 <id1>::map <int>::type() << endl;

This compiles fine and prints 0 in both cases, on clang 3.3, gcc 4.8.1, gcc 4.9.0.

In most cases, my workarounds have been introducing an intermediate template struct before the alias. However, I am now trying to use metafunctions to parametrize generic SFINAE tests and in this case I have to use aliases directly, because structs should not be instantiated. Just to get an idea, a piece of the actual code is here.

ISO C++11 14.3.3/1:

A template-argument for a template template-parameter shall be the name of a class template or an alias template, expressed as id-expression.

Plus I don't see any special exceptions for variadic template template parameters.

On the other hand, such template aliases cause serious problems in real-world code that I cannot reproduce here: frequent internal compiler errors for gcc, and less frequent unexpected behavior for clang (only in more advanced SFINAE tests).

Root of problems can be in other places. You should try to localize code which causes internal compiler error - just remove unrelated parts one by one (or use some kind of binary search, i.e. divide-and-conquer) - and check if error is still here on each stage.


As for GCC 4.9.0 error, try to change

template <typename... T>
using map = F <T...>;

to

template <typename... U>
using map = F <U...>;

Maybe this would help to understand what GCC sees.