Maa*_*lis 12 c++ gcc clang variadic-templates c++17
std=c++17作为唯一的编译器标志编译的代码片段...
问题:这是Clang编译器中的错误,还是GCC接受此代码是否错误?还是其他原因?
#include <functional>
#include <tuple>
template <typename... Ts>
struct Foo
{
template <typename T>
using Int = int;
// Function that accepts as many 'int' as there are template parameters
using Function = std::function< void(Int<Ts>...) >;
// Tuple of as many 'int' as there are template parameters
using Tuple = std::tuple< Int<Ts>... >;
auto bar(Function f)
{
std::apply(f, Tuple{}); // Error with Clang 8.0.0
}
};
int main()
{
auto foo = Foo<char, bool, double>{};
foo.bar([](int, int, int){});
}
Run Code Online (Sandbox Code Playgroud)
令我感到奇怪的是,Clang的错误表明它成功地别名Tuple为,std::tuple<int, int, int>但错误地别名Function为just std::function<void(int)>,仅使用一个参数而不是三个参数。
In file included from <source>:2:
In file included from /opt/compiler-explorer/gcc-8.3.0/lib/gcc/x86_64-linux-gnu/8.3.0/../../../../include/c++/8.3.0/functional:54:
/opt/compiler-explorer/gcc-8.3.0/lib/gcc/x86_64-linux-gnu/8.3.0/../../../../include/c++/8.3.0/tuple:1678:14: error: no matching function for call to '__invoke'
return std::__invoke(std::forward<_Fn>(__f),
^~~~~~~~~~~~~
/opt/compiler-explorer/gcc-8.3.0/lib/gcc/x86_64-linux-gnu/8.3.0/../../../../include/c++/8.3.0/tuple:1687:19: note: in instantiation of function template specialization 'std::__apply_impl<std::function<void (int)> &, std::tuple<int, int, int>, 0, 1, 2>' requested here
return std::__apply_impl(std::forward<_Fn>(__f),
^
<source>:19:14: note: in instantiation of function template specialization 'std::apply<std::function<void (int)> &, std::tuple<int, int, int> >' requested here
std::apply(f, Tuple{}); // Error
^
<source>:26:9: note: in instantiation of member function 'Foo<char, bool, double>::bar' requested here
foo.bar([](int, int, int){});
Run Code Online (Sandbox Code Playgroud)
正如评论中的其他用户所指出的那样,使Int模板别名依赖于类型可以T解决此问题:
template <typename T>
using Int = std::conditional_t<true, int, T>;
Run Code Online (Sandbox Code Playgroud)
我发现的其他内容,仅从Function外部引用类型也使它按预期/期望的方式工作:
int main()
{
auto f = Foo<char, bool, double>::Function{};
f = [](int, int, int){};
}
Run Code Online (Sandbox Code Playgroud)
TL;DR:这是一个 bug,但标准中也存在一个 bug。
\n\n首先要注意,在 C++ 中,模板分两步处理:
\n\n现在,clang 似乎将其视为std::function< void(Int<Ts>...) >非依赖类型,原因如下:
Int<Ts>是非依赖类型(正确)。Int<Ts>因此,包含(即)的包扩展Int<Ts>...也是一种非依赖“类型”(?)。void(Int<Ts>...)都是非依赖的,所以它是非依赖类型(显然是不正确的)。std::function是非依赖的,并且模板参数void(Int<Ts>...)是非依赖的非包扩展类型,std::function< void(Int<Ts>...) >所以是非依赖类型。(请注意,“非包扩展”检查使其与情况不同Tuple。)
因此,当Foo定义时,类型名称Function被视为命名非依赖类型,并立即构建,并且不考虑包扩展(在实例化期间发生)。因此,所有使用的 都Function被替换为“脱糖”类型std::function< void(int) >。
此外,clang具有实例化相关的概念,这意味着构造是不依赖的,但它仍然以某种方式涉及模板参数(例如,构造仅对某些参数有效)。std::function< void(Int<Ts>...) >被视为依赖于实例化的类型,因此当模板被实例化时,clang 仍然执行对 的替换using Function = std::function< void(Int<Ts>...) >。结果,Function获得了正确的类型,但这不会传播到 的Function定义中的使用Foo。
现在这是标准中的错误。
\n\n类型是否依赖在[temp.dep.type]中定义:
\n\n\n\n\n一个类型是依赖的,如果它是
\n\n\n
\n- 模板参数,
\n- 未知专业的成员,
\n- 作为当前实例化的依赖成员的嵌套类或枚举,
\n- 一个 cv 限定类型,其中 cv 不限定类型是从属的,
\n- 由任何依赖类型构造的复合类型,
\n- 其元素类型是相关的或其边界(如果有)是值相关的数组类型,
\n- 其异常规范与值相关的函数类型,
\n- 由simple-template-id表示,其中模板名称是模板参数,或者任何模板参数是依赖类型或依赖于类型或值依赖的表达式,或者是包扩展 [\ xe2\x80\x89注意:这包括不使用template-argument-list的\n 类模板的注入类名。\xe2\x80\x94\xe2\x80\x89 尾注\xe2\x80\x89] ,\n 或
\n- 用 表示
\ndecltype(expression),其中表达式与类型相关。
请注意,它并没有说参数列表包含包扩展的函数类型是依赖类型,只是说“从任何依赖类型构造的复合类型”和“异常规范依赖于值的函数类型”是依赖的。两者在这里都没有帮助。
\n