Руководство по варидической дедукции, не принятое g++, взято clang++ - кто прав?
Рассмотрим следующий код:
template <typename... Types>
struct list
{
template <typename... Args>
list(Args...)
{
static_assert(sizeof...(Types) > 0);
}
};
template <typename... Args>
list(Args...) -> list<Args...>;
int main()
{
list l{0, 0.1, 'a'};
}
Я ожидаю, что decltype(l)
будет list<int, double, char>
. К сожалению, g++ 7.2 и g++ trunk не дают статического утверждения. clang++ 5.0.0 и clang++ trunk компилируются и работают как ожидалось.
вид соответствия godbolt.org
Это ошибка g++? Или есть причина, почему руководство по вычету не следует соблюдать здесь?
Добавление ограничения SFINAE для конструктора, по-видимому, обеспечивает желаемое поведение:
template <typename... Args,
typename = std::enable_if_t<sizeof...(Args) == sizeof...(Types)>>
list(Args...)
{
static_assert(sizeof...(Types) > 0);
}
вид соответствия godbolt.org
Ответы
Ответ 1
Это gcc ошибка 80871. Проблема в том, что мы заканчиваем этот набор кандидатов для вычета:
template <class... Types, class... Args>
list<Types...> __f(Args... ); // constructor
template <class... Args>
list<Args...> __f(Args... ); // deduction-guide
Оба действительны (Types...
может выводить как пустые в первом случае), но вызов здесь должен быть неоднозначным - ни один из них не является более специализированным, чем другой. Types...
здесь не участвует (похоже на пример в [temp.deduct.partial]/12). Поэтому правильное поведение - перейти к следующему тай-брейку, который выступает за вычитание-руководства. Следовательно, это должно быть list<int, double, char>
.
Однако поведение gcc должно способствовать конструктору, поэтому триггеры static_assert
becusase Types...
действительно были бы пустыми в этой ситуации.