不同 C++ 编译器之间的自动类型推导不匹配
Mismatched deduction of auto types between different c++ compilers
因此,我尝试以现代 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 and also: 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]
:
if the type that replaces the placeholder type is not the same in each deduction, the program is ill-formed
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
上失败会很棒。)
根据我的评论展开:
g++ 并不总是这样做,考虑示例 auto i = 0l, f = 0.0;
,它给出了错误:
test.cpp: In function ‘int main()’:
test.cpp:4:5: error: inconsistent deduction for ‘auto’: ‘long int’ and then ‘double’
4 | auto i = 0l, f = 0.0;
如果我们编译您的程序并打印变量的类型 (with this method),我们将得到以下输出:
v1: std::initializer_list<int>, i1: int const*
v2: std::initializer_list<int>, i2: int const*
使用 gcc 版本 9.2.0,带有标志 -std=c++17 -pedantic -Wall -Wextra
,没有任何警告或错误。
根据您对标准的评论,该程序格式错误,标准 specifies 应该发出诊断消息(警告或错误),除非另有说明(在这种情况下不是).因此我会说这是 gcc 中的一个错误。
ICC 上的 static_assert
失败绝对是一个错误。我通过将 static_assert
移动到一个单独的函数中找到了一个简单的解决方法。不是很优雅的解决方案,但它有效。
稍作修改后,这是使用 GCC、Clang 和 ICC 编译的代码:
template<std::size_t size, class... Args>
void args_no_guard(Args... args)
{
static_assert(sizeof...(args) == size);
}
template<class... Args>
auto dot(Args... args)
{
return [=](auto... brgs)
{
constexpr auto n = sizeof...(args);
args_no_guard<n>(brgs...);
using T = std::common_type_t<decltype(args)..., decltype(brgs)...>;
const T v1[]{static_cast<T>(args)...};
const T v2[]{static_cast<T>(brgs)...};
T dot = 0;
for (std::size_t i = 0; i < n; ++i)
dot += v1[i] * v2[i];
return dot;
};
}
因此,我尝试以现代 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 and also: 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]
:
if the type that replaces the placeholder type is not the same in each deduction, the program is ill-formed
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
上失败会很棒。)
根据我的评论展开:
g++ 并不总是这样做,考虑示例 auto i = 0l, f = 0.0;
,它给出了错误:
test.cpp: In function ‘int main()’:
test.cpp:4:5: error: inconsistent deduction for ‘auto’: ‘long int’ and then ‘double’
4 | auto i = 0l, f = 0.0;
如果我们编译您的程序并打印变量的类型 (with this method),我们将得到以下输出:
v1: std::initializer_list<int>, i1: int const*
v2: std::initializer_list<int>, i2: int const*
使用 gcc 版本 9.2.0,带有标志 -std=c++17 -pedantic -Wall -Wextra
,没有任何警告或错误。
根据您对标准的评论,该程序格式错误,标准 specifies 应该发出诊断消息(警告或错误),除非另有说明(在这种情况下不是).因此我会说这是 gcc 中的一个错误。
ICC 上的 static_assert
失败绝对是一个错误。我通过将 static_assert
移动到一个单独的函数中找到了一个简单的解决方法。不是很优雅的解决方案,但它有效。
稍作修改后,这是使用 GCC、Clang 和 ICC 编译的代码:
template<std::size_t size, class... Args>
void args_no_guard(Args... args)
{
static_assert(sizeof...(args) == size);
}
template<class... Args>
auto dot(Args... args)
{
return [=](auto... brgs)
{
constexpr auto n = sizeof...(args);
args_no_guard<n>(brgs...);
using T = std::common_type_t<decltype(args)..., decltype(brgs)...>;
const T v1[]{static_cast<T>(args)...};
const T v2[]{static_cast<T>(brgs)...};
T dot = 0;
for (std::size_t i = 0; i < n; ++i)
dot += v1[i] * v2[i];
return dot;
};
}