Оператор неявного преобразования против конструктора шаблона - кто должен иметь приоритет? - PullRequest
0 голосов
/ 12 июня 2018

Рассмотрим следующий фрагмент кода:

template <typename>
struct dependent_false { static constexpr auto value = false; };

struct foo
{
    foo() { }

    template <typename T>
    foo(const T&) { static_assert(dependent_false<T>::value, ""); }
};

struct proxy
{
    operator foo() { return foo{};  }
};

int main()
{
    (void) foo{proxy{}};
}

При компиляции с -std=c++17:

  • clang++ (транк) успешно компилирует код;

  • g++ (транк) не может скомпилировать код - он создает экземпляр foo(const T&).

При компиляции с -std=c++11 оба компилятора отклоняюткод.Новые правила материализации prvalue в C ++ 17 могут повлиять на поведение здесь.

живой пример на godbolt.org


Какое здесь правильное поведение?

  • Гарантирует ли Стандарт, что foo::foo(const T&) будет (или не будет) создан?

  • Гарантирует ли Стандарт, что оператор неявного преобразования предпочтительнее вызова foo::foo(const T&), независимо от того, был ли он создан?

1 Ответ

0 голосов
/ 12 июня 2018

Это CWG 2327 :

Рассмотрим пример, подобный:

struct Cat {};
struct Dog { operator Cat(); };

Dog d;
Cat c(d);

Это относится к 11.6 [dcl.init] bullet 17.6.2:

В противном случае, если инициализация является прямой инициализацией, или если это инициализация копированием, когда не квалифицированная по cv версия исходного типа является тем же классом или производным классом,Класс назначения, конструкторы считаются.Применимые конструкторы перечислены (16.3.1.3 [over.match.ctor]), и лучший из них выбирается с помощью разрешения перегрузки (16.3 [over.match]).Выбранный таким образом конструктор вызывается для инициализации объекта, с выражением инициализатора или списком выражений в качестве аргументов.Если конструктор не применяется, или разрешение перегрузки является неоднозначным, инициализация неверна.

Разрешение перегрузки выбирает конструктор перемещения Cat.Инициализация параметра Cat && конструктора приводит к временному, в соответствии с 11.6.3, пулу [dcl.init.ref] 5.2.1.2.Это исключает возможность исключения копии в этом случае.

Кажется, это упущение в изменении формулировки для гарантированного исключения копии.Вероятно, в этом случае мы должны одновременно рассматривать как конструкторы, так и функции преобразования, как и при инициализации копирования, но нам нужно убедиться, что это не вызывает каких-либо новых проблем или неясностей.

Я считаю, что clang реализует это подразумеваемое изменение (и поэтому считает функцию преобразования более подходящим), а gcc - нет (и поэтому никогда не рассматривает функцию преобразования).

Согласно стандарту, gcc верен.

...