Различная логика производится clang и gcc для одного и того же кода.Что правильно? - PullRequest
0 голосов
/ 01 февраля 2019

Я обнаружил несоответствие между логикой, создаваемой gcc-8 и clang-6.

Это произошло в реальной базе кода, когда я разработал с использованием clang, я развернул его с помощью gcc.

Пожалуйста, сообщите, какой компилятор содержит ошибку, чтобы я мог правильно сообщить об ошибке.

Конспект

A неявно преобразуется в B A и может быть из A (копирование / перемещение) и std::initializer_list<B>.

При инициализацииA из A&&:

  • clang выбирает конструктор перемещения
  • gcc выбирает конструктор initializer_list.

живая демонстрация: https://coliru.stacked -crooked.com / a / bc50bd8f040d6476

MCVE

#include <initializer_list>
#include <utility>
#include <iostream>

struct thing;

struct thing_ref
{
    thing_ref(thing&& other) : ref_(other) {}
    thing_ref(thing& other) : ref_(other) {}

    thing& ref_;
};

struct thing
{
    thing() {}

    thing(std::initializer_list<thing_ref> things)
    {
        std::cout << "initializer_list path\n";
    }

    thing(thing&& other)
    {
        std::cout << "move path\n";
    }

    thing(thing const& other)
    {
        std::cout << "copy path\n";
    }
};

struct foo
{
    foo(thing t) : mything { std::move(t) } {}
    thing mything;
};

int main()
{
    thing t;

    auto f = foo { std::move(t) };
}

Настройки компилятора:

Ничего особенного, согласно coliruссылка: -std=c++17 -O2

1 Ответ

0 голосов
/ 01 февраля 2019

Стандартный черновик (T - thing) [dcl.init.list] :

Инициализация списка - это инициализация объекта или ссылки из фигурной скобки.-init-лист....

Инициализация списка объекта или ссылки типа T определяется следующим образом:

  • Если в фигурном списке инициализации содержится назначенный инициализатор-список [не применяется]

  • Если T является агрегатным классом и [не применяется]

  • В противном случае,если T является массивом символов [не применяется]

  • В противном случае, если T является агрегатом [не применяется]

  • В противном случае, если в списке инициализатора нет элементов [не применяется]

  • В противном случае, если T является специализацией std::initializer_­list<E> [не применяется]

  • В противном случае, если T является типом класса, рассматриваются конструкторы.Применимые конструкторы перечисляются, и лучший выбирается с помощью разрешения перегрузки [применяется]

  • ...

[over.match.list] :

Когда объекты неагрегированного класса типа T инициализируются списком так, что [dcl.init.list] указывает, что разрешение перегрузки выполняется в соответствии с правилами, изложенными в этом подпункте, разрешение перегрузки выбирает конструктор в два этапа:

  • Первоначально функции-кандидаты инициализируют-list конструкторы ([dcl.init.list]) класса T и список аргументов состоит из списка инициализаторов как единственного аргумента. [применяется]

  • Если не найден жизнеспособный конструктор списка инициализаторов , разрешение перегрузки выполняется снова, гдевсе функции-кандидаты являются конструкторами класса T, а список аргументов состоит из элементов списка инициализаторов.

Если список инициализаторов не имеет элементов и T имеет конструктор по умолчанию,первая фаза опущена.[не применяется]

Вернитесь к [dcl.init.list], чтобы узнать, что такое конструктор списка инициализатора :

Конструктор - это конструктор списка инициализаторов, если его первый параметр имеет тип std::initializer_­list<E> или ссылку на, возможно, квалифицированную по cv std::initializer_­list<E> для некоторого типа E, и либо отсутствуют другие параметры, либо все остальные параметрыимеют аргументы по умолчанию ([dcl.fct.default]).

Также имеется удобное примечание, подтверждающее вывод:

Примечание: конструкторы списка инициализаторапредпочтительнее других конструкторов при инициализации списка

Мой вывод:

Кандидат конструктора списка инициализатора должен рассматриваться первым и использоваться, если он действителен.Поскольку thing неявно преобразуется в thing_ref, оно должно быть действительным.Мне кажется, что GCC соответствует.

Если вы хотите инициализировать объект типа, который имеет конструктор списка инициализатора, но не хотите использовать этот конструктор, то не используйте инициализацию списка, т.е.не используйте скобку-init-list.

...