[英]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以及: 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
c 還是卡住了:
<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'
所以我想聽聽您的意見:
感謝您閱讀這個長長的問題。 (作為獎勵,如果有人能回答為什么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;
如果我們編譯您的程序並打印變量的類型( 使用此方法),我們會得到以下 output:
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
沒有任何警告或錯誤。
根據您對標准的評論,該程序格式錯誤,並且標准指定應該發出診斷消息(警告或錯誤),除非另有說明(在這種情況下不是)。 因此我會說這是 gcc 中的一個錯誤。
這是一個已知的錯誤。
ICC 上的static_assert
失敗絕對是一個錯誤。 我通過將static_assert
移動到單獨的 function 中找到了一個簡單的解決方法。 不是很優雅的解決方案,但它有效。
稍作修改,這是與 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;
};
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.