Обоснование универсальной эталонной функции шаблона является более жизнеспособным соответствием, чем не шаблонная функция?
Учитывая функцию-член шаблона (... шаблонный конструктор в шаблонном классе, как это происходит), принимающий универсальную переменную ссылку, и другую функцию-кандидат (в моем случае конструктор копирования), и GCC, и Clang выбирают универсальный шаблон ссылки в разрешение перегрузки при копировании объекта. Поскольку два совершенно разных компилятора ведут себя одинаково, я предполагаю, что это действительно "правильная вещь".
Пример кода на https://godbolt.org/g/mip3Fi.
Таким образом, когда копия сделана, есть две жизнеспособные функции-кандидаты с одинаковым количеством параметров, оба с идеальным соответствием (после свертывания ссылок для версии шаблона), из которых одна является кандидатом на специализацию шаблона функции, а другая - нет., И, конечно, из которых призвание одного "явно желательно", тогда как призвание другого - нет.
Мое понимание разрешения перегрузки состоит в том, что среди набора функций-кандидатов должен быть выбран лучший жизнеспособный кандидат, и в случае, если шаблон и не шаблонный кандидат, которые эквивалентно жизнеспособны, конкурируют, выбирается не шаблонный кандидат. Это был бы конструктор копирования.
Очевидно, что компилятор (и, вероятно, стандарт?) Думает иначе. В чем причина этого довольно неочевидного исключения?
Бонусный вопрос:
Как я могу предотвратить это, то есть как сказать компилятору, что я действительно хочу вызывать конструктор копирования при создании копии?
Что-то вроде enable_if_t<!is_same<container<T> const& blahblah...> blah>
устраняет плохого кандидата и копирует работу по назначению, но этот подход не слишком полезен, потому что он не скомпилирует момент, когда вы вызываете шаблон с переменным числом аргументов более чем одним аргументом (что является его первоначальной целью!).
Написание initializer_list
Функция осведомленности вместо этого будет работать вокруг проблемы, но это превратит идеально перенаправленное emplace в копию и перемещение, что тоже не совсем хорошо.
РЕДАКТИРОВАТЬ:
(вышеуказанный связанный код по запросу)
#include <cstdio>
template<typename T> struct container
{
container() { puts("default"); }
container(container const&) { puts("copy"); /* blah */ }
template<typename... U> container(U&&... args) { puts("template"); /* new T[sizeof...(args)]{std::forward<T>(args)...}; blah blah */ }
};
int main()
{
container<int> f;
container<int> g{1,2,3};
container<int> h(f); // <--- copy constructor most viable form?!
return 0;
}