GCC 7.2 y clang 5.0 parecen no estar de acuerdo con el siguiente código cuando se compilan con -std=c++17 -Werror -Wconversion:

#include <iostream>
#include <string>
#include <string_view>

int main(int argc, char* argv[]) {
    std::string s = "Hello, World";
    std::string_view vs{s};
    std::cout << std::string{vs} << '\n';
    return EXIT_SUCCESS;
}

Compilado con clang++ -std=c++17 -Wall -Werror -Wconversion, se compila correctamente (consulte https://godbolt.org/g/JZn8cL)

Sin embargo, GCC 7.2 emite un diagnóstico algo desconcertante (consulte https://godbolt.org/g/kmYbJ3 ):

<source>: In function 'int main(int, char**)':
7 : <source>:7:26: error: choosing 'std::__cxx11::basic_string<_CharT, _Traits, _Alloc>::operator std::__cxx11::basic_string<_CharT, _Traits, _Alloc>::__sv_type() const [with _CharT = char; _Traits = std::char_traits<char>; _Alloc = std::allocator<char>; std::__cxx11::basic_string<_CharT, _Traits, _Alloc>::__sv_type = std::basic_string_view<char>]' over 'constexpr std::basic_string_view<_CharT, _Traits>::basic_string_view(const std::basic_string_view<_CharT, _Traits>&) [with _CharT = char; _Traits = std::char_traits<char>]' [-Werror=conversion]
     std::string_view vs{s};
                          ^
7 : <source>:7:26: error:   for conversion from 'std::__cxx11::string {aka std::__cxx11::basic_string<char>}' to 'std::basic_string_view<char>' [-Werror=conversion]
7 : <source>:7:26: note:   because conversion sequence for the argument is better
cc1plus: all warnings being treated as errors
Compiler exited with result code 1

Ambos compiladores compilan bien el código después de cambiar vs{s} a vs(s).

¿Qué compilador está aquí? ¿La inicialización de llaves de un std::string_view de un std::string está en conflicto con las reglas de conversión de alguna manera, y clang no emite un diagnóstico cuando podría / debería? ¿O GCC está equivocado y el diagnóstico es erróneo?

3
acm 14 nov. 2017 a las 21:06

2 respuestas

La mejor respuesta

¿Qué compilador está aquí?

¿Ambos? Los compiladores pueden dar advertencias, o no, según lo deseen. Es solo que cuando escribes:

std::string_view vs{s};

Es posible que tenga la impresión de que esto está haciendo algo como una inicialización agregada o al menos invocando un constructor string_view, pero no lo es, y tal vez esa impresión no es tan probable que suceda si escribe:

std::string_view vs(s);

Entonces gcc te da una advertencia (ya que pediste una).

El código está bien. Ambos compiladores están bien. Simplemente use () s para inicializar, no hay razón para usar {} aquí (no hay diferencia de comportamiento en los dos enfoques).

2
Barry 14 nov. 2017 a las 21:45

Su pregunta original ("¿es esto C ++ inválido?") Ahora ha sido respondida ("no"), pero está claro por los comentarios que ahora está interesado en por qué gcc advierte sobre este código válido. Esa es una pregunta más interesante.

Al eliminar los argumentos de la plantilla, la advertencia se reduce a esto:

warning: choosing 'std::string::operator std::string_view()' over 'constexpr std::string_view(const std::string_view&) ' [-Wconversion]

Por lo tanto, es una advertencia sobre la elección de un operador de conversión definido por el usuario sobre un constructor ... pero ese constructor en realidad no se puede usar para la conversión. Aquí hay otro ejemplo que advierte por el mismo motivo:

class A {  // Like string_view
public:
    A() {}
};
class B {  // Like string
public:
    operator A() {return A();}
};

int main() {
    B b;
    A a{b};
}

Esto da:

<source>: In function 'int main()':
12 : <source>:12:10: warning: choosing 'B::operator A()' over 'constexpr A::A(A&&)' [-Wconversion]
     A a{b};
          ^
12 : <source>:12:10: warning:   for conversion from 'B' to 'A' [-Wconversion]
12 : <source>:12:10: note:   because conversion sequence for the argument is better
12 : <source>:12:10: warning: choosing 'B::operator A()' over 'constexpr A::A(const A&)' [-Wconversion]
12 : <source>:12:10: warning:   for conversion from 'B' to 'A' [-Wconversion]
12 : <source>:12:10: note:   because conversion sequence for the argument is better

Por cierto, agregando p. Ej. El constructor A(int) no agrega nada a la advertencia, por lo que se centra en el constructor de movimiento y el constructor de copia de A. Eliminar los constructores de copiar y mover con =delete tampoco cambia la advertencia.

Esto aclara qué está causando la advertencia, pero no por qué advierte. La documentación de gcc para -Wconversion dice:

Advierte de conversiones implícitas que pueden alterar un valor. Esto incluye conversiones entre reales y enteros, como abs (x) cuando x es doble; conversiones entre firmados y sin firmar, como unsigned ui = -1; y conversiones a tipos más pequeños, como sqrtf (M_PI). No advierta sobre conversiones explícitas como abs ((int) x) y ui = (unsigned) -1, o si la conversión no cambia el valor como en abs (2.0). Las advertencias sobre conversiones entre enteros firmados y sin firmar se pueden inhabilitar usando -Wno-sign-conversion.

Para C ++, también advierte sobre la resolución confusa de sobrecarga para las conversiones definidas por el usuario; y conversiones que nunca usan un operador de conversión de tipo: conversiones a void, el mismo tipo, una clase base o una referencia a ellas. Las advertencias sobre conversiones entre enteros con y sin signo están deshabilitadas de manera predeterminada en C ++ a menos que -Wsign-conversion esté habilitado explícitamente.

Por tanto, depende de lo que considere "resolución de sobrecarga confusa para conversiones definidas por el usuario". Quizás los desarrolladores de gcc consideran que A a{b} parece una llamada a un constructor de A, por lo que consideran que llamar a B::operator A() es "confuso".

O tal vez sea de hecho un error ... Dado que esta parece ser una bandera de advertencia rara vez utilizada (ni siquiera está en -Wall), y dado lo extraños que son los mensajes de "elegir x sobre y", eso es bastante posible.

0
Arthur Tacca 15 nov. 2017 a las 13:33