¿Por qué gcc y clang producen resultados diferentes para este programa? (operador de conversión vs constructor)


Programa:

#include <stdio.h>

struct bar_t {
    int value;
    template<typename T>
    bar_t (const T& t) : value { t } {}

    // edit: You can uncomment these if your compiler supports
    //       guaranteed copy elision (c++17). Either way, it 
    //       doesn't affect the output.

    // bar_t () = delete;
    // bar_t (bar_t&&) = delete;
    // bar_t (const bar_t&) = delete;
    // bar_t& operator = (bar_t&&) = delete;
    // bar_t& operator = (const bar_t&) = delete;
};

struct foo_t {
    operator int   () const { return 1; }
    operator bar_t () const { return 2; }
};

int main ()
{
    foo_t foo {};
    bar_t a { foo };
    bar_t b = static_cast<bar_t>(foo);

    printf("%d,%d\n", a.value, b.value);
}

Salida para gcc 7/8:

2,2

Salida para clang 4/5 (también para gcc 6.3)

1,1

Parece que lo siguiente está sucediendo al crear las instancias de bar_t:

For gcc, it calls foo_t::operator bar_t then constructs bar_t with T = int.

For clang , it constructs bar_t with T = foo_t then calls foo_t::operator int

¿Qué compilador es correcto aquí? (o tal vez ambos son correctos si esto es alguna forma de comportamiento indefinido)

Author: verb_noun, 2017-05-19

1 answers

Creo que el resultado de Clang es correcto.

Tanto en bar_t a { foo } direct-list-initialization como en un static_cast entre tipos definidos por el usuario, los constructores del tipo de destino se consideran antes que los operadores de conversión definidos por el usuario en el tipo de origen (C++14 [dcl.init.list] / 3 [expr.estática.cast] / 4). Si la resolución de sobrecarga encuentra un constructor adecuado, se usa.

Al hacer la resolución de sobrecarga bar_t::bar_t<foo_t>(const foo_t&) es viable y será una mejor coincidencia que una para cualquier instanciación de esto plantilla que resulta en el uso de los operadores de fundición en foo. También será mejor que cualquier constructor declarado por defecto ya que toman algo que no sea foo_t, por lo que se usa bar_t::bar_t<foo_t>.


El código tal como está escrito actualmente depende de C++17 guaranteed copy elision; Si compila sin C++17 guaranteed copy elision (por ejemplo, -std=c++14) entonces clang rechaza este código debido a la inicialización de copia en bar_t b = static_cast<bar_t>(foo);.

 18
Author: bames53,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-19 01:04:26