【发布时间】:2020-03-10 08:15:00
【问题描述】:
所以,我正在尝试以某种现代 C++ 风格实现点积 (https://en.wikipedia.org/wiki/Dot_product),并提出以下代码:
#include <iostream>
template<class... Args>
auto dot(Args... args)
{
auto a = [args...](Args...)
{
return [=](auto... brgs)
{
static_assert(sizeof...(args) == sizeof...(brgs));
auto v1 = {args...}, i1 = v1.begin();
auto v2 = {brgs...}, i2 = v2.begin();
typename std::common_type<Args...>::type s = 0;
while( i1 != v1.end() && i2!= v2.end())
{
s += *i1++ * *i2++;
}
return s;
};
};
return a(std::forward<Args>(args)...);
}
int main()
{
auto a = dot(1,3,-5)(4,-2,-1);
std::cout << a << std::endl;
}
在线:https://gcc.godbolt.org/z/kDSney 以及:cppinsights
上面的代码在g++ 下编译和执行得很好,但是clang(以及icc 和msvc)卡住了它:
clang++ ./funcpp.cpp --std=c++17
./funcpp.cpp:12:4: error: 'auto' deduced as 'std::initializer_list<int>' in declaration of
'v1' and deduced as 'const int *' in declaration of 'i1'
auto v1 = {args...}, i1 = v1.begin();
^ ~~~~~~~~~ ~~~~~~~~~~
./funcpp.cpp:28:11: note: in instantiation of function template specialization
'dot<int, int, int>' requested here
auto a = dot(1,3,-5)(4,-2,-1);
^
1 error generated.
现在,如果我打破v1、v2、i1、i2 的定义,就像:
auto v1 = {args...} ;
auto i1 = v1.begin();
auto v2 = {brgs...};
auto i2 = v2.begin();
clang 和 msvc 没有问题,icc 仍然窒息:
<source>(10): error: static assertion failed
static_assert(sizeof...(args) == sizeof...(brgs));
^
detected during instantiation of "auto dot(Args...) [with Args=<int, int, int>]" at line 30
compilation aborted for <source> (code 2)
Execution build compiler returned: 2
但是,如果我删除了有问题的static_assert,那么icc 编译代码也没有问题。
除了(典型的)问题:这是对的,为什么:) 具体问题是:
根据[dcl.spec.auto]:
如果每次推导中替换占位符类型的类型都不相同,则程序是病态的
clang 正确识别出有问题的行中定义了两种不同的类型:'auto' deduced as 'std::initializer_list<int>' in declaration of 'v1' and deduced as 'const int *' in declaration of 'i1',所以我想听听您的意见:
- 考虑到这种特定情况(https://gcc.gnu.org/onlinedocs/gcc-9.2.0/gcc/C_002b_002b-Extensions.html#C_002b_002b-Extensions 中未提及),我是否遇到了一些未记录的 g++ 扩展,因为据我所知 g++ 正确处理了自动声明列表中的不同类型,
- 或者 g++ 没有推断出这两种类型是不同的(...嗯...)
- 还是别的什么?
感谢您阅读这个冗长的问题。
(作为奖励,如果有人能回答为什么 icc 在 static_assert 上失败,那就太好了。)
【问题讨论】:
-
这里
std::forward<Args>(args)有什么用? -
test.cpp:在函数“int main()”中:test.cpp:4:5:错误:“auto”的推导不一致:“long int”,然后是“double” 4 |自动 i = 0l,f = 0.0; | ^~~~ 用g++,所以看起来一般不会扩展这个。
-
打印类型给我们:std::initializer_list
, int const* std::initializer_list , int const* in g++,所以它推导出不同的类型。 -
GCC does not compile
auto v = { 1, 2, 3 }, i = v.begin();。不明白它编译了相同的 insiede lambda。最小示例:gcc.godbolt.org/z/a5XyxU。它甚至可以在用户定义的函子内编译:gcc.godbolt.org/z/eYutyK,或模板函数:gcc.godbolt.org/z/jnEYXh。 -
@underscore_d 我想是的。最小的例子是
template <typename T> void f(T a) { auto v = {a}, i = v.begin(); },当被调用时,例如f(1);。重写为void f(int a) { /* same body */ }会导致编译错误。
标签: c++ gcc clang auto type-deduction