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).
std::forward<Args>(args)
aquí?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, comof(1);
. Reescrito comovoid f(int a) { /* same body */ }
causa error de compilación.Respuestas:
Ampliando de mis comentarios:
g ++ no siempre hace esto, considere el ejemplo
auto i = 0l, f = 0.0;
, da el error:Si compilamos su programa e imprimimos los tipos de variables ( con este método ), obtenemos el siguiente resultado:
usando gcc versión 9.2.0, con banderas
-std=c++17 -pedantic -Wall -Wextra
sin ninguna advertencia o error.Por su comentario sobre el estándar, este programa está mal formado y el estándar especifica que debe emitirse un mensaje de diagnóstico (advertencia o error) a menos que se especifique lo contrario (que no es así, en este caso). Por lo tanto, diría que este es un error en gcc.
Es un error conocido .
fuente
g++
de esto.La
static_assert
falla en ICC es definitivamente un error. Encontré una solución simple al pasarstatic_assert
a una función separada. Solución no muy elegante, pero funciona.Con ligeras modificaciones, este es el código que se compila con GCC, Clang e ICC:
fuente