Проблемы с выводом аргументов шаблона для шаблонов классов

Я читал статью о выводе аргументов шаблона для шаблонов классов здесь http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0091r3.html. Эта функция входит в стандарт C ++ 17, и некоторые вещи меня смущали.

template <typename T>
class Something {
public:

    // delete the copy and move constructors for simplicity
    Something(const Something&) = delete;
    Something(Something&&) = delete;

    explicit Something(T&&) { ... }
    explicit Something(const T&) { ... }

    template <typename U, typename EnableIfNotT<U, T>* = nullptr>
    Something(U&&) { ... }
};

Учитывая приведенный выше код, если кто-то попытается создать экземпляр указанного выше шаблона следующим образом

auto something = Something{std::shared_ptr<int>{}};

всегда ли будет вызываться перегрузка ссылки rvalue? Поскольку набор перегрузок, рассматриваемый для вычета, равен

template <typename T>
Something<T> F(T&&) { ... }
template <typename T>
Something<T> F(const T&) { ... }
template <typename T, typename U, typename EnableIfNotT<U, T>*>
Something<T> F(U&&) { ... }
  1. Вторая перегрузка никогда не будет предпочтительнее первой (поскольку теперь это перегрузка ссылки пересылки, а не перегрузка ссылки rvalue), так что же здесь должно происходить?
  2. И похоже, что последний никогда не может быть вызван без явного указания параметра T, это предполагаемое поведение?
  3. Также есть ли какие-либо другие ошибки или рекомендации по стилю, которые следует учитывать при использовании вывода аргументов шаблона для шаблонов классов?
  4. Кроме того, должны ли определяемые пользователем руководства по дедукции располагаться после определения класса? Например, можно ли указать конечный возвращаемый тип в объявлении конструктора класса в самом определении класса? (В отличие от конструктора итератора здесь http://en.cppreference.com/w/cpp/language/class_template_deduction)

person Curious    schedule 03.06.2017    source источник
comment
(1) Вторая перегрузка фактически такая же, как и третья перегрузка, т.е. даже если вы передадите lvalue, она выберет третью. (2) Нет, просто передайте конструктору lvalue для вызова третьего. (3) Время покажет. Думаю, пока что мало кто пользуется им. (4) Похоже, но я не вижу для этого причины. Возможно, это изменится в будущем.   -  person Henri Menke    schedule 03.06.2017
comment
Кроме того, в вашем примере ничего не может работать вообще, потому что все конструкторы являются частными, а определение EnableIfNotT отсутствует (я думаю, это std::enable_if_t<!std::is_same<U, T>::value>)   -  person Henri Menke    schedule 03.06.2017
comment
@HenriMenke добавил public, и это то, что включало, если было. Я не имел в виду, что это должно быть в центре внимания вопроса. Как будет выводиться первый аргумент шаблона в третьем случае с обычным lvalue?   -  person Curious    schedule 03.06.2017
comment
@HenriMenke, включить if - это просто, но нужно добавить еще std::decay_t   -  person Curious    schedule 03.06.2017
comment
Единственное, что вам не следует читать для этой функции, - это этот документ. Чтобы исправить спецификацию, потребовалось еще две бумаги.   -  person T.C.    schedule 03.06.2017
comment
@ T.C. Не могли бы вы дать мне ссылку на последнее описание этой функции? Страница cppreference - лучшее место для просмотра?   -  person Curious    schedule 03.06.2017
comment
Что ж, рабочий документ был бы лучше всего, но если вам это не нравится, страница cppreference выглядит достаточно полной, и я не вижу никаких очевидных проблем с ней.   -  person T.C.    schedule 03.06.2017
comment
@ T.C. прочтите страницу cppreference, все еще кажется, что проблема преобразования ссылки rvalue на проблему ссылки пересылки все еще существует ..   -  person Curious    schedule 03.06.2017
comment
Вы явно недостаточно внимательно его прочитали. Прямо со страницы: ссылка rvalue на параметр шаблона cv-unqualified не является ссылкой пересылки, если этот параметр является параметром шаблона класса.   -  person T.C.    schedule 03.06.2017
comment
Что вы имеете в виду, говоря, всегда ли будет вызываться перегрузка ссылки rvalue ?? Вы показали сценарий только один раз, что же здесь означает «всегда»?   -  person Johannes Schaub - litb    schedule 03.06.2017
comment
@ JohannesSchaub-litb Я имел в виду. Подобно тому, как функции пересылки ссылок предпочтительнее функций, принимающих const T&, даже если они «кажутся» лучше подходят для неконстантных lvalue. Например, wandbox.org/permlink/8KvHZXIjflD7pLXY.   -  person Curious    schedule 03.06.2017


Ответы (1)


  1. Вторая перегрузка никогда не будет предпочтительнее первой (поскольку теперь это перегрузка ссылки пересылки, а не перегрузка ссылки rvalue), так что же здесь должно произойти?

Нет, это не ссылка для пересылки. Это ключевое различие. Из [temp.deduct.call]:

Ссылка на пересылку - это ссылка rvalue на параметр шаблона cv-unqualified, который не представляет параметр шаблона шаблона класса (во время вывода аргумента шаблона класса ([over.match.class.deduct])) .

Ваши кандидаты:

template <typename T>
Something<T> F(T&&);       // this ONLY matches non-const rvalues

template <typename T>
Something<T> F(const T&);  // this matches everything

template <typename T, typename U, typename EnableIfNotT<U, T>*>
Something<T> F(U&&);       // this matches nothing

Когда вы пишете:

auto something = Something{std::shared_ptr<int>{}};

Конструктор T&& является предпочтительным с T=std::shared_ptr<int>, поэтому вы получите Something<std::shared_ptr<int>> в качестве специализации шаблона вашего класса. Если бы вместо этого было написано:

std::shared_ptr<int> p;
auto something = Something{p};

тогда предпочтительнее использовать конструктор T const& (действительно, это единственный жизнеспособный кандидат). Хотя попадаем в одно и то же место: Something<std::shared_ptr<int>>.

  1. И похоже, что последний никогда не может быть вызван без явного указания параметра T, это предполагаемое поведение?

Правильно, T - это невыведенный контекст. В этом есть смысл - этот конструктор существует для выполнения преобразований, но вам нужно указать, что вы преобразовываете в, чтобы выполнить преобразование. Было бы никогда не иметь смысла, чтобы это «просто работало» на вас.

  1. Кроме того, должны ли определяемые пользователем руководства по дедукции располагаться после определения класса?

да. Вот куда они идут, по правилу. Нет смысла иметь конечный возвращаемый тип в конструкторе - конструктор ничего не «возвращает».

person Barry    schedule 03.06.2017
comment
Спасибо за ответ! Значит, правила пересылки ссылок были обновлены в связи с этим изменением? Еще одна вещь смутила меня из включенной вами ссылки. В определении struct A в связанном блоке текста, как T&& в № 3 является ссылкой для пересылки? T правильно ли указан параметр шаблона класса (и, следовательно, не должен быть ссылкой на пересылку)? - person Curious; 03.06.2017
comment
@Curious Это не параметр шаблона класса, это параметр шаблона руководства по выводам. - person Barry; 03.06.2017
comment
Итак, когда вы пишете руководство по вычету, то, что не было ссылкой для пересылки, теперь становится ссылкой для пересылки? Мне кажется, это нелогично, тебе не кажется? Есть ли у вас какое-то представление о том, почему это так? - person Curious; 03.06.2017
comment
Кроме того, теперь, когда в стандарте есть обязательное исключение копирования, я чувствую, что включение конечного возвращаемого типа в конструктор не является полностью неправильным ... Поскольку конструктор оценивает значение prvalue, точно так же, как функция, возвращающая значение. - person Curious; 03.06.2017
comment
@Curious Нет того, что было ... теперь становится. Это не связанные вещи. В template <class T> struct A { A(T&& ); } T не является ссылкой для пересылки - есть конструктор, который принимает ссылку rvalue на T. Эти отношения просто сохраняются при дедукции. Ничего не изменилось. - person Barry; 03.06.2017
comment
Итак, когда вы добавляете руководство по выводу ссылок переадресации, тогда предыдущий конструктор rvalue никогда не будет предпочтительнее нового конструктора ссылок пересылки, правильно? И определение предыдущего конструктора rvalue также не предполагает, что параметр является ссылкой пересылки. - person Curious; 03.06.2017
comment
@Curious Что ж, конструктор rvalue более специализирован, поэтому в случае rvalues ​​он будет предпочтительнее. - person Barry; 03.06.2017
comment
Все еще немного запутался, когда вы добавляете руководство по дедукции, как в примере, когда вы переходите к определению конструкторов, вам нужно будет определить 3 вместо 2? Я думал, что руководство по дедукции должно было быть для существующего конструктора, а не для отдельной функции. - person Curious; 03.06.2017
comment
@Curious Руководство по дедукции не для существующего конструктора - это для дедукции. Это только необходимо, если конструкторов недостаточно для вывода (например, конструктор пары итераторов для std::vector). Его единственная цель - направлять дедукцию. Ваши конструкторы должны быть определены на основе того, что вам действительно нужно для создания вашего класса. - person Barry; 03.06.2017
comment
Значит, вы по-прежнему определяете только два конструктора в этом примере для struct A? И в конструкторе rvalue вы не ожидаете ссылки на переадресацию для типа T, поскольку вы предполагаете, что это rvalue, но руководство по выводу теперь будет соответствовать почти всему этому, поскольку согласно руководству по выводу T теперь является ссылкой пересылки вместо ссылка на rvalue? - person Curious; 03.06.2017
comment
@ Любопытно, извини, я не понимаю, о чем ты говоришь. - person Barry; 03.06.2017
comment
вот что я имею в виду, ideone.com/a9Cb7h, какие конструкторы будут вызываться здесь main()? - person Curious; 03.06.2017
comment
@Curious Что вас смущает в поведении этой программы? - person Barry; 03.06.2017
comment
Разве в руководстве по выводам не сказано, что параметр шаблона T теперь является ссылкой пересылки, а не ссылкой rvalue? По крайней мере, это то, что я сделал из примера в предоставленной вами ссылке. - person Curious; 03.06.2017
comment
@Curious Я даже не понимаю, что означает этот вопрос. Вот лучший пример. U&& - это ссылка для пересылки, T&& не является ссылкой для пересылки - это ссылка rvalue на T. - person Barry; 03.06.2017
comment
Итак, я думаю, что я пытаюсь понять, что это вообще значит для U&& быть ссылкой для пересылки? Поскольку конструктор с параметром ссылки пересылки вообще не может быть вызван - person Curious; 03.06.2017
comment
ЭТО НЕ КОНСТРУКТОР. ЭТО НИЧЕГО НЕ ДОЛЖЕН СВЕТИТЬСЯ СО СТРОИТЕЛЬСТВОМ. Руководство по вычету существует ТОЛЬКО для выбора того, A<T> вы строите. ПОСЛЕ того, как вы выберете T, ЗАТЕМ вы вернетесь и выберете конструктор. Deducing A{integer} выбирает руководство по дедукции как лучшее соответствие, которое дает вам U = int&, так что вы получаете _5 _..., тогда мы в основном делаем A<int>{integer}, который вызывает конструктор const T&. - person Barry; 03.06.2017
comment
Понял, спасибо. Извините за непонятный ряд вопросов. Я думал, что это тот же конструктор, но только с возвращаемым типом, объясняющим, каким будет параметр шаблона для класса. - person Curious; 03.06.2017