`apply` 模板在 g++ 中编译,但在 clang++ 和 vc++ 中不编译
`apply` template compiles in g++ but not in clang++ and vc++
下面的代码compiles successfully在g++ 7.2.0中(编译标志是-std=c++14 -Wall -Wextra -Werror -pedantic-errors
),但是在中编译失败clang++ 5.0.0(具有相同的标志,-std=c++14 -Wall -Wextra -Werror -pedantic-errors
)和vc++ 15.4(编译标志为/EHsc /Za /std:c++14 /permissive-
) :
template <template <typename...> class Functor, typename... FixedArguments>
struct apply
{
template <typename... FreeArguments>
using type = Functor<FixedArguments..., FreeArguments...>;
};
template <typename, typename>
struct Bar{};
template <template <typename...> class>
struct Foo{};
int main()
{
(void)Foo<apply<Bar, int, char>::type>{};
}
哪种编译器行为符合标准?如何更改这样的模板 apply
以在 clang++ 上编译?
clang++ 错误信息:
5 : <source>:5:15: error: too many template arguments for class template 'Bar'
using type = Functor<FixedArguments..., FreeArguments...>;
^ ~~~~~~~~~~~~~~~~~
16 : <source>:16:15: note: in instantiation of template class 'apply<Bar, int, char>' requested here
(void)Foo<apply<Bar, int, char>::type>{};
^
9 : <source>:9:8: note: template is declared here
struct Bar{};
vc++ 错误信息:
5 : <source>(5): error C2977: 'Bar': too many template arguments
9 : <source>(9): note: see declaration of 'Bar'
16 : <source>(16): note: see reference to class template instantiation 'apply<Bar,int,char>' being compiled
注意: 看了这个之后,如果 Bar 是别名模板而不是 class 模板,这个答案就是正确的。解决方法有效,但出于其他原因。请参阅构造函数答案以获取 OP 的正确答案。
这个问题被称为 'alias flaw',我们在 kvasir::mpl 的实施过程中遇到了很多挑战。问题是 Bar 正好需要两个参数,但 sizeof...(FixedArguments)+sizeof...(FreeArguments)
加起来可能不等于 2。
编译器可以通过所有别名调用尝试跟踪潜在的元数,并且仅在用户实际传递 2 以外的内容时才发出错误,或者它可以 "eagerly" 简单地通过证明错误可能会给出错误发生。
我发现解决这个问题的有效方法是根据输入的大小进行别名调用https://godbolt.org/g/PT4uaE
template<bool>
struct depends{
template<template<typename...> class F, typename...Ts>
using f = F<Ts...>;
};
template<>
struct depends<false>{
template<template<typename...> class F, typename...Ts>
using f = void;
};
template <template <typename...> class Functor, typename... FixedArguments>
struct apply
{
template <typename... FreeArguments>
using type = typename depends<(sizeof...(FixedArguments)+sizeof...(FreeArguments) == 2)>::template f<Functor, FixedArguments..., FreeArguments...>;
};
template <typename, typename>
struct Bar{};
template <template <typename...> class>
struct Foo{};
int main()
{
(void)Foo<apply<Bar, int, char>::type>{};
}
应该注意的是,我测试过的所有编译器都不需要限制为正好两个,一个可以很容易地限制为 sizeof...(FixedArguments)+sizeof...(FreeArguments) != 100000
,并且编译器仍然会接受它,只在出现错误时发出错误实际上并没有在具体的通话中解决问题。
实际上,我想改进我对内部工作方式的心智模型,以便提出更快的解决方法,在 kvasir::mpl 中,我们目前正在尝试在幕后手动跟踪元数,以便消除会稍微减慢速度的依赖调用。
因为 @T.C. 这样的代码格式不正确(不需要诊断)。
C++14 标准,部分 "Name resolution" [temp.res],第 8 段:
If every valid specialization of a variadic template requires an empty
template parameter pack, the template is ill-formed, no diagnostic
required.
C++ 标准的最新草案,部分 "Name resolution" [temp.res],第 8.3 段:
...The program is ill-formed, no diagnostic required, if:
- ...
- every valid specialization of a variadic template requires an empty template parameter pack...
附加信息:Core Issue 2067。
按照标准要求这样简单的workaround可以写成:
template <template <typename...> class Functor, typename... Arguments>
struct invoke
{
using type = Functor<Arguments...>;
};
template <template <typename...> class Functor, typename... FixedArguments>
struct apply
{
template <typename... FreeArguments>
using type = typename invoke<Functor, FixedArguments..., FreeArguments...>::type;
};
更新: 由于 @odinthenerd 此变通方法使用了导致程序编译速度变慢的附加类型。
下面的代码compiles successfully在g++ 7.2.0中(编译标志是-std=c++14 -Wall -Wextra -Werror -pedantic-errors
),但是在中编译失败clang++ 5.0.0(具有相同的标志,-std=c++14 -Wall -Wextra -Werror -pedantic-errors
)和vc++ 15.4(编译标志为/EHsc /Za /std:c++14 /permissive-
) :
template <template <typename...> class Functor, typename... FixedArguments>
struct apply
{
template <typename... FreeArguments>
using type = Functor<FixedArguments..., FreeArguments...>;
};
template <typename, typename>
struct Bar{};
template <template <typename...> class>
struct Foo{};
int main()
{
(void)Foo<apply<Bar, int, char>::type>{};
}
哪种编译器行为符合标准?如何更改这样的模板 apply
以在 clang++ 上编译?
clang++ 错误信息:
5 : <source>:5:15: error: too many template arguments for class template 'Bar' using type = Functor<FixedArguments..., FreeArguments...>; ^ ~~~~~~~~~~~~~~~~~ 16 : <source>:16:15: note: in instantiation of template class 'apply<Bar, int, char>' requested here (void)Foo<apply<Bar, int, char>::type>{}; ^ 9 : <source>:9:8: note: template is declared here struct Bar{};
vc++ 错误信息:
5 : <source>(5): error C2977: 'Bar': too many template arguments 9 : <source>(9): note: see declaration of 'Bar' 16 : <source>(16): note: see reference to class template instantiation 'apply<Bar,int,char>' being compiled
注意: 看了这个之后,如果 Bar 是别名模板而不是 class 模板,这个答案就是正确的。解决方法有效,但出于其他原因。请参阅构造函数答案以获取 OP 的正确答案。
这个问题被称为 'alias flaw',我们在 kvasir::mpl 的实施过程中遇到了很多挑战。问题是 Bar 正好需要两个参数,但 sizeof...(FixedArguments)+sizeof...(FreeArguments)
加起来可能不等于 2。
编译器可以通过所有别名调用尝试跟踪潜在的元数,并且仅在用户实际传递 2 以外的内容时才发出错误,或者它可以 "eagerly" 简单地通过证明错误可能会给出错误发生。
我发现解决这个问题的有效方法是根据输入的大小进行别名调用https://godbolt.org/g/PT4uaE
template<bool>
struct depends{
template<template<typename...> class F, typename...Ts>
using f = F<Ts...>;
};
template<>
struct depends<false>{
template<template<typename...> class F, typename...Ts>
using f = void;
};
template <template <typename...> class Functor, typename... FixedArguments>
struct apply
{
template <typename... FreeArguments>
using type = typename depends<(sizeof...(FixedArguments)+sizeof...(FreeArguments) == 2)>::template f<Functor, FixedArguments..., FreeArguments...>;
};
template <typename, typename>
struct Bar{};
template <template <typename...> class>
struct Foo{};
int main()
{
(void)Foo<apply<Bar, int, char>::type>{};
}
应该注意的是,我测试过的所有编译器都不需要限制为正好两个,一个可以很容易地限制为 sizeof...(FixedArguments)+sizeof...(FreeArguments) != 100000
,并且编译器仍然会接受它,只在出现错误时发出错误实际上并没有在具体的通话中解决问题。
实际上,我想改进我对内部工作方式的心智模型,以便提出更快的解决方法,在 kvasir::mpl 中,我们目前正在尝试在幕后手动跟踪元数,以便消除会稍微减慢速度的依赖调用。
因为 @T.C.
C++14 标准,部分 "Name resolution" [temp.res],第 8 段:
If every valid specialization of a variadic template requires an empty template parameter pack, the template is ill-formed, no diagnostic required.
C++ 标准的最新草案,部分 "Name resolution" [temp.res],第 8.3 段:
...The program is ill-formed, no diagnostic required, if:
- ...
- every valid specialization of a variadic template requires an empty template parameter pack...
附加信息:Core Issue 2067。
按照标准要求这样简单的workaround可以写成:
template <template <typename...> class Functor, typename... Arguments>
struct invoke
{
using type = Functor<Arguments...>;
};
template <template <typename...> class Functor, typename... FixedArguments>
struct apply
{
template <typename... FreeArguments>
using type = typename invoke<Functor, FixedArguments..., FreeArguments...>::type;
};
更新: 由于 @odinthenerd