[C++] ¿Por qué está fallando gcc cuando se usa lambda para un parámetro de plantilla sin tipo?


Answers

Estoy de acuerdo con la respuesta de Nir y quería agregarle algo de información. Cita la sección relevante en el estándar (§14.3.2 [temp.arg.nontype]) que muestra que ya no es necesario que los parámetros que no son de tipo tengan un enlace, pero eso aún no muestra que GCC se está portando mal para la parte lambda Para eso tenemos que mostrar que static_cast<FUNCT>(lambda) es una expresión constante convertida. Para eso necesitamos un borrador más nuevo del que Nir vinculó. Y esta sección

§5.1.5 Expresiones de Lambda [expr.prim.lambda]:

  1. El tipo de cierre para una expresión lambda no genérica sin captura lambda tiene una función de conversión a puntero para funcionar con enlace de lenguaje C ++ (7.5) que tiene el mismo parámetro y tipos de retorno que el operador de llamada a función del tipo de cierre. [...] La función de conversión es pública, constexpr, no virtual, no explícita, const y tiene una especificación de excepción que no arroja.

Curiosamente, GCC afirma haber implementado esto ( N4268 ) en la versión 6 ya lanzada (en caso de que quiera excusar el comportamiento de GCC diciendo que GCC 7 aún no se ha lanzado oficialmente, así que tal vez cuando salga esto lo haga). ser reparado):

Language Feature                                               Proposal  Available in GCC?  SD-6 Feature Test
Allow constant evaluation for all non-type template arguments  N4268     6                  __cpp_nontype_template_args >= 201411

Entonces, en resumen, esto es un error en GCC.

Question

El siguiente fragmento se compila sin error con Clang 4.0, pero GCC 7.0 produce errores (observe el uso del indicador -std = c ++ 1z).

using FuncT = int (*)(double);

template <FuncT FUNC>
int temp_foo(double a)
{
    return FUNC(a);
}

int foo(double a)
{
    return 42;
}

void func()
{
    auto lambda = [](double a) { return 5; };

    struct MyStruct
    {
        static int foo(double a) { return 42; }
    };

    temp_foo<foo>(3);
    temp_foo<static_cast<FuncT>(lambda)>(3);
    temp_foo<MyStruct::foo>(3);
}

Específicamente, GCC se queja de que tanto el método lambda como el de la clase anidada no tienen vinculación, por lo que no se pueden usar como un argumento de plantilla sin tipo.

Al menos para el caso lambda creo que Clang es correcto (y GCC está equivocado) desde (citando de cppreference , el operador de conversión):

El valor devuelto por esta función de conversión es un puntero a una función con enlace de lenguaje C ++ que, cuando se invoca, tiene el mismo efecto que invocar directamente al operador de llamada de función del objeto de cierre.

¿Se está portando mal GCC?