Entonces, estoy tratando de implementar el producto punto ( https://en.wikipedia.org/wiki/Dot_product ) en un poco de C ++ moderno y se me ocurrió el siguiente código:
#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;
}
En línea: https://gcc.godbolt.org/z/kDSney y también: cppinsights
El código anterior se compila y se ejecuta muy bien con g++
, sin embargo clang
( icc
y msvc
) estrangularlo:
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.
Ahora bien, si rompo la definición de v1
, v2
, i1
, i2
como:
auto v1 = {args...} ;
auto i1 = v1.begin();
auto v2 = {brgs...};
auto i2 = v2.begin();
clang
y msvc
no tengo problemas, icc
aún se ahoga:
<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
Sin embargo, si elimino al infractor static_assert
, icc
tampoco tendrá problemas para compilar el código.
Y al lado de la pregunta (típica): cuál es la razón y por qué :) la pregunta concreta es:
De acuerdo a [dcl.spec.auto]
:
Si el tipo que reemplaza el tipo de marcador de posición no es el mismo en cada deducción, el programa está mal formado
clang
identificó correctamente que hay dos tipos diferentes definidos en la línea en cuestión: 'auto' deduced as 'std::initializer_list<int>' in declaration of 'v1' and deduced as 'const int *' in declaration of 'i1'
así que me gustaría escuchar sus opiniones si:
- ¿Llegué a alguna extensión de g ++ no documentada teniendo en cuenta esta situación específica (no mencionada en https://gcc.gnu.org/onlinedocs/gcc-9.2.0/gcc/C_002b_002b-Extensions.html#C_002b_002b-Extensions ) ya que g ++ que yo sepa maneja correctamente los diferentes tipos en una lista de declaración automática,
- o por casualidad g ++ no dedujo que los dos tipos son diferentes (... hm ...)
- ¿o algo mas?
Gracias por leer esta larga pregunta. (Como beneficio adicional, si alguien pudiera responder por qué icc
falla static_assert
, sería genial).
auto v = { 1, 2, 3 }, i = v.begin();
. No entiendo que compila el mismo insiede lambda. Ejemplo mínimo: gcc.godbolt.org/z/a5XyxU . Incluso se compila dentro de un functor definido por el usuario: gcc.godbolt.org/z/eYutyK , o una función de plantilla: gcc.godbolt.org/z/jnEYXh .
template <typename T> void f(T a) { auto v = {a}, i = v.begin(); }
, cuando se invoca, por ejemplo, como f(1);
. Reescrito como void f(int a) { /* same body */ }
causa error de compilación.
std::forward<Args>(args)
aquí?