Почему gcc и clang выдают разные результаты для этой программы? (оператор преобразования против конструктора)

Программа:

#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);
}

вывод для gcc 7/8:

2,2

выход для clang 4/5 (также для gcc 6.3)

1,1

Кажется, что следующее происходит при создании экземпляров bar_t:

Для GCC это calls foo_t::operator bar_t затем constructs bar_t with T = int,

Для лязга это constructs bar_t with T = foo_t затем calls foo_t::operator int

Какой компилятор здесь правильный? (или, возможно, они оба верны, если это какая-то форма неопределенного поведения)

1 ответ

Решение

Я считаю, что результат Clang является правильным.

В обоих bar_t a { foo } прямая инициализация списка и в static_cast между пользовательскими типами конструкторы типа назначения рассматриваются перед пользовательскими операторами преобразования для исходного типа (C++14 [dcl.init.list]/3 [expr.static.cast]/4). Если разрешение перегрузки находит подходящий конструктор, он используется.

При выполнении разрешения перегрузки bar_t::bar_t<foo_t>(const foo_t&) является жизнеспособным и будет лучше соответствовать любому экземпляру этого шаблона, что приведет к использованию операторов приведения в foo. Это также будет лучше, чем любые объявленные по умолчанию конструкторы, так как они принимают что-то другое foo_t, так bar_t::bar_t<foo_t> используется.


Код в том виде, в котором он написан в настоящее время, зависит от гарантированного разрешения C++17; Если вы компилируете без C++17 гарантированное разрешение копирования (например, -std=c++14) затем Clang отклоняет этот код из-за инициализации копирования в bar_t b = static_cast<bar_t>(foo);,

Другие вопросы по тегам