std :: is_constructible devuelve un valor inconsistente para el constructor privado

13

¿Cuáles son las reglas por las cuales se std::is_constructiblemanejan los constructores privados? Dado el siguiente código:

#include <iostream>

class Class {
private:
    Class() { }
};

template <typename T>
class Test {
public:
    static void test() {
        std::cout
            //<< std::is_constructible<Class>::value
            << std::is_constructible<T>::value
            << std::endl;
    }
};

int main() {
    Test<Class>::test();
}

Esto imprime 0( ideone ), es decir, Tno es construible por defecto.

Descomentando la línea comentada, imprime 11( ideone ), por lo que de Trepente se convirtió en construible por defecto.

Podría encontrar un razonamiento para respaldar ambos resultados, pero no entiendo cómo incluir la línea comentada cambia el resultado del segundo. ¿Esto de alguna manera invoca a UB? ¿Es esto un error del compilador? ¿O es std::is_constructiblerealmente tan inconsistente?

zennehoy
fuente
1
Parece un error de GCC, el clang imprime 900
Yksisarvinen
1
Otro pensamiento extraño que noto al compilar en mi máquina con c ++ 17 g ++ 9.2.1 / g ++ - 10.0 y reemplazar std :: is_constructible <...> :: value con is_constructible_v <...>, es que el resultado cambia a 00
mutableVoid
1
@mutableVoid De hecho, y parece que la ::valueversión también es capaz de cambiar la salida de los que vienen antes: godbolt.org/z/zCy5xU Descomente la línea comentada y todo se convierte en 1: s en gcc.
Ted Lyngmo
1
Otra forma de solucionarlo: godbolt.org/z/EKaP3r, así que básicamente se trata de algún tipo de error de orden de evaluación.
Marek R
2
@mutableVoid Ni siquiera necesita crear una instancia de la plantilla de función para que se haga realidad. En este ejemplo, regresa falsepero si la plantilla de función no está comentada, de repente regresa true: godbolt.org/z/zqxdk2
Ted Lyngmo

Respuestas:

3

std::is_constructibledebería regresar falseen este escenario porque el constructor no es accesible.

Como se señaló debajo de la pregunta, el comportamiento descrito en la pregunta es causado por un error en GCC / libstdc ++. El error se informa aquí y, de acuerdo con Bugzilla, está relacionado con, si no es causado por un error de control de acceso para clases en funciones de plantilla que no se ha resuelto durante bastante tiempo. La relación entre los dos errores se toma del comentario de Jonathan Wakely sobre Bugzilla, que parece haber detectado primero la conexión entre los dos errores.

Esto también está implícito en el hecho de que el comportamiento de este escenario en GCC se vuelve correcto al eliminar el constructor en lugar de hacerlo privado:

class Class {
    Class() = delete;
};

que imprime 0y 00respectivamente. Este es el resultado correcto (que también clanginforma correctamente en el escenario con un constructor privado).

Esto podría explicar el cambio de comportamiento observado al comentar en la línea, porque dentro de la función en la estructura con plantilla , la comprobación de acceso no funciona e informa que el constructor es accesible cuando no lo es. Cuando el rasgo se verifica nuevamente en la siguiente línea o posiblemente en una ubicación completamente diferente (como es el caso aquí ), ya se ha instanciado y, por lo tanto, produce la respuesta incorrecta.

mutableVoid
fuente