So, I am trying to implement the dot product (https://en.wikipedia.org/wiki/Dot_product) in some flavour of modern C++ and came up with the following code:
#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;
}
Online: https://gcc.godbolt.org/z/kDSney and also: cppinsights
The code above compiles and executes nicely with g++
, however clang
(and icc
and msvc
) choke on it:
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.
Now, if I break up the definition of v1
, v2
, i1
, i2
like:
auto v1 = {args...} ;
auto i1 = v1.begin();
auto v2 = {brgs...};
auto i2 = v2.begin();
clang
and msvc
have no problems, icc
still chokes:
<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
However if I remove the offending static_assert
then icc
has no issues compiling the code either.
And beside of the (typical) question: which is right and why :) the concrete question is:
According to [dcl.spec.auto]
:
if the type that replaces the placeholder type is not the same in each deduction, the program is ill-formed
clang
correctly identified that there are two different types defined in the line in question: 'auto' deduced as 'std::initializer_list<int>' in declaration of 'v1' and deduced as 'const int *' in declaration of 'i1'
so I'd like to hear your opinions whether:
Thanks for reading through this long question.
(As a bonus if someone could answer why icc
fails on the static_assert
would be great.)
Expanding from my comments:
g++ does not do this always, consider the example auto i = 0l, f = 0.0;
, it gives the error:
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;
If we compile your program and print the types of the variables (with this method), we get the following output:
v1: std::initializer_list<int>, i1: int const*
v2: std::initializer_list<int>, i2: int const*
using gcc version 9.2.0, with flags -std=c++17 -pedantic -Wall -Wextra
without any warning or error.
By your comment of the standard this program is ill-formed and the standard specifies that there should be emitted a diagnostic message (warning or error) unless otherwise specified (which it is not, in this case). Hence I would say that this is a bug in gcc.
It is a known bug.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With