Почему конструктор шаблона предпочтительнее копировать конструктор?

#include 

struct uct
{
    uct() { std::cerr << "default" << std::endl; }

    uct(const uct &) { std::cerr << "copy" << std::endl; }
    uct(      uct&&) { std::cerr << "move" << std::endl; }

    uct(const int  &) { std::cerr << "int" << std::endl; }
    uct(      int &&) { std::cerr << "int" << std::endl; }

    template 
    uct(T &&) { std::cerr << "template" << std::endl; }
};

int main()
{
    uct u1    ; // default
    uct u2( 5); // int
    uct u3(u1); // template, why?
}

coliru

Перегрузка шаблона конструктора подходит для обоих объявлений (u2 и u3). Но когда int передается в конструктор, выбирается не шаблонная перегрузка. Когда вызывается конструктор копирования, выбирается перегрузка шаблона. Насколько я знаю, не шаблонная функция всегда предпочтительнее шаблонной функции во время разрешения перегрузки. Почему конструктор копирования обрабатывается по-другому?

27
задан TylerH 13 September 2019 в 14:58
поделиться

3 ответа

Насколько я знаю, нешаблонная функция всегда предпочитается для шаблонной обработки функции во время разрешения перегрузки.

Это верно, только когда специализация и не обрабатывает по шаблону, точно то же. Дело обстоит не так здесь все же. Когда Вы звоните uct u3(u1), наборы перегрузки добираются

uct(const uct &)
uct(uct &) // from the template

Теперь, так как u1 не константа, которую это должно было бы применить преобразование константы для вызова конструктора копии. Назвать шаблонную специализацию - потребности ничего не сделать, так как это - точное совпадение. Это означает шаблонные победы, поскольку это - лучшее соответствие.

Для остановки этой вещи можно сделать, использовать SFINAE для ограничения шаблонной функции, которую только назовут, когда T не uct. Это было бы похоже

template <typename T, std::enable_if_t<!std::is_same_v<uct, std::decay_t<T>>, bool> = true>
uct(T &&) { std::cerr << "template" << std::endl; }
33
ответ дан 27 November 2019 в 18:37
поделиться

, Когда конструктора копии судят, чтобы быть названным, шаблонная перегрузка выбрана. Насколько я знаю, что нешаблонная функция всегда предпочитается для шаблонной обработки функции во время разрешения перегрузки. Почему конструктор копии обрабатывается по-другому?

template <typename T>
uct(T &&) { std::cerr << "template" << std::endl; }
//    ^^

причина, шаблонная версия выбрана, состоит в том, потому что компилятор способен
для генерации конструктора с подписью (T &), которая соответствует лучше и поэтому выбрана.

  • при изменении подписи с uct u1 до const uct u1 тогда, она соответствовала бы конструктору копии (так как u1 не константа для начала).

  • при изменении подписи с uct(const uct &) до uct(uct&), это было бы лучшее соответствие, и это предпочло бы это шаблонной версии.

  • кроме того, эти uct(uct&&) был бы выбран, если Вы использовали uct u3(std::move(u1));

<час>

Для фиксации этого, можно использовать SFINAE для отключения перегрузки, когда T совпадает с uct:

template <typename T, std::enable_if_t<!std::is_same_v<std::decay_t<T>, uct>>>
uct(T&&)
{
  std::cerr << "template" << std::endl;
}
4
ответ дан 27 November 2019 в 18:37
поделиться

Проблема состоит в том, что у шаблонного конструктора есть не квалификация const, в то время как у нешаблонного конструктора копии есть константа спецификатора в ее параметре. Если Вы объявите объект u1 как const объект тогда, нешаблонного конструктора копии вызовут.

Из Стандарта C++ (7 Стандартных преобразований)

1 Стандартное преобразование является неявными преобразованиями со встроенным значением. Пункт 7 перечисляет полный набор таких преобразований. Стандартная последовательность преобразования является последовательностью стандартных преобразований в следующем порядке:

(1.4) — Нулю или одному преобразованию квалификации

Так конструктор копии нужно одно стандартное преобразование, в то время как шаблонный конструктор sies не требует такого преобразования.

2
ответ дан 27 November 2019 в 18:37
поделиться
Другие вопросы по тегам:

Похожие вопросы: