Closed apolukhin closed 3 years ago
yndx-antoshkka, 5 июля 2017, 16:29 А как именно это должно выглядеть?
Павел, 5 июля 2017, 18:16 yndx-antoshkka,
void foo(const std::string&... words) {
(std::cout << ... << words) << std::endl;
}
yndx-antoshkka, 5 июля 2017, 19:21 Павел Корозевцев, тут есть проблема:
void foo(int...);
Такой код сейчас собирается, за счёт правила, позволяющего не ставить запятую и трактуется как
void foo(int /*var*/, ...);
С variadic templates всё верно работает за счёт контекстно чувствительной грамматики
template <int... I> // compiler: remembering that `I` is a pack
void foo(I......); // compiler: `I` is a pack => void foo(I... /*vars*/, ...);
В вашем примере void foo(const std::string&...)
компилятор не сможет догадаться о ваших намерениях. Нужно как-то ему намекнуть + неплохо было бы показать пользователю, что функция на самом то деле шаблонная (см ваш же комментарий ниже)
Andrey Davydov, 6 июля 2017, 0:33 yndx-antoshkka, теоретически, для этой цели по аналогии с std::initializer_list можно ввести специальный магический тип std::varargs. Тогда пример с выводом слов мог бы выглядеть так:
template<size_t N>
void foo(std::varargs<std::string const &, N> words) {
for (auto const & w : words) std::cout << w;// constexpr for из p0589
std::cout << std::endl;
}
Однако меня пугает, то что overload resolution усложнится еще больше, особенно при инициализации объектов.
struct Container
{
template<typename T> Container(std::initializer_list<T>); // #1
template<typename T> Container(std::size_t size, T value); // #2
template<typename T, std::size_t N> Container(std::varargs<T, N>); // #3
};
Container c2 {1, 2}; // #1
Container c1 (1, 2); // #2 или #3
Antervis, 6 июля 2017, 9:16 yndx-antoshkka, разумеется, чем проще тем лучше. Идеальный вариант - void func(int ...args);
Я так полагаю, проблема в том, что дополнительно требуется явно указать что функция-шаблон (подобная проблема, если не ошибаюсь, с синтаксисом типа void func(auto arg);). Синтаксис template (без скобок) уже используется для явного инстанцирования, синтаксис template <> (без аргументов) уже используется для template overloading'а. template
Тогда синтаксис будет вида
template <...>
void func(int ..args) { /*...*/ }
Павел, 5 июля 2017, 18:20 А зачем это делать? Это всё равно будет шаблон. Всё равно инстанциироваться будет на этапе компиляции, как и шаблоны. Если вы хотите гарантировать, что в функцию не попадёт "неправильный" тип, то это можно и сейчас делать.
Такой синтаксис подарит нам ограничения, но не даст фич. Или я ошибаюсь?
Andrey Davydov, 5 июля 2017, 23:15 Павел Корозевцев, гарантировать что в функцию не попадет неправильный тип можно, но заставить, чтобы вывелся правильный тип -- нет. Воображаемую функцию void foo(const std::string&... words); можно будет вызвать так:
foo("aba", "caba"),
а такую
template<typename... Args,
typename = std::enable_if_t<std::conjunction_v<std::is_same<std::string, Args>...>>>
void foo(Args const & ...);
нельзя.
Antervis, 6 июля 2017, 8:22 Павел Корозевцев, проблема в другом. Если у нас есть func(string ...s), мы сможем передать в неё func(s1, s2, {v1.begin(),v1.end()}) и код скомпилируется. С variadic template'ом последний аргумент попросту не получится передать
Александр, 6 июля 2017, 8:25 Andrey Davydov,
template<typename ... Args, typename = std::enable_if_t<std::conjunction_v<std::is_constructible<std::string, Args>...>>>
void foo(Args &&...words)
{
(std::cout << ... << words) << std::endl;
}
Andrey Davydov, 6 июля 2017, 8:45 croessmah, в Вашем варианте все равно не заработает пример от @Antervis комментарием выше.
Александр, 7 июля 2017, 13:51 Andrey Davydov, я когда писал, его поста еще не было. Да и других проблем это не решает.
rymis, 11 июля 2017, 13:46 Удалил свой предыдущий комментарий, чтобы написать корректно.
Я недавно решал подобную проблему так:
#include <iostream>
#include <string>
#include <utility>
using namespace std;
template <typename...T>
inline void print_impl(const typename pair<T, string>::second_type&...args) {
std::string strings[] = {args...};
for (auto&& s : strings)
cout << s;
cout << endl;
}
template <typename...T>
inline void print(T&&...vals) {
print_impl<T...>(std::forward<T>(vals)...);
}
int main() {
print("Hello", " ", "World");
}
Так это работает вполне пристойно. Конечно выглядит не очень, но проблема решаема в рамках текущего стандарта C++.
Antervis, 12 июля 2017, 15:44 rymis, ваш пример не решает braced-initialization проблему, описанную выше:
string s = "somestring";
print({s.begin(), s.end()});
Так не сработает.
C С++20 terse syntax для концептов появилась возможность указывать параметры паков, как на точное соответствие
void exact(std::same_as<std::string> auto const& ... strings) {}
так и на не точное:
void inexact(std::convertible_to<std::string> auto const& ... strings) {}
Песочница https://godbolt.org/z/1ThEahxeY
braced-initialization правда не разрешён, но это возможно и к лучшему, слишком уж много неоднозначностей появляется
Перенос предложения: голоса +8, -3 Автор идеи: Antervis
разрешить конструкцию типа void func(MyType ...args) { ... };
Добавить возможность писать variadic template функции, принимающие variadic pack фиксированного типа.