Clang не может вывести пустые вариационные аргументы шаблона, не помещенные в конец аргументов функции [duplicate]

У меня была такая же проблема. Длина моей колонки была слишком короткой. Что вы можете сделать, это либо увеличить длину, либо сократить текст, который вы хотите поместить в базу данных.

12
задан HighCommander4 8 February 2013 в 11:00
поделиться

4 ответа

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

Таким образом, два аргумента 0, 0 сравниваются с , int, что дает несоответствие.

Правила дедукции, подобные этой, должны охватывать множество особых случаев (например, что происходит, когда два пакета параметров появляются рядом друг с другом). Поскольку пакеты параметров являются новой функцией в C ++ 11, авторы соответствующего предложения разработали правила консервативно.

Обратите внимание, что пакет параметров шаблона завершающего шаблона будет пустым, если он иначе не выводится. Поэтому, когда вы вызываете конструктор с одним аргументом, все будет работать (обратите внимание на разницу в пакете параметров шаблона и в пакете параметров функции здесь. Первый заголовок, последний - нет).

9
ответ дан Johannes Schaub - litb 25 August 2018 в 07:59
поделиться

На самом деле меня немного интересует одно и то же (желая специализироваться на шаблонных пакетах параметров на основе последних аргументов).

Я считаю, что может быть путь вперед, комбинируя разворот набора (std::make_tuple , back-port std::apply для C ++ 14 и т. д.):

Вернется сюда, если он будет успешным.

Похожие сообщения:

РЕДАКТОР: Да, выяснилось, не идеально, так как есть дополнительные копии, но это начало.

Если вы знаете более простой способ, чем то, что я перечислю ниже, пожалуйста, не стесняйтесь публиковать сообщения!

TL; DR

Можно делать такие вещи:

auto my_func_callable = [] (auto&& ... args) {
    return my_func(std::forward<decltype(args)>(args)...);
};
auto my_func_reversed =
    stdcustom::make_callable_reversed(my_func_callable);

И затем реализовать этот код pseduo:

template<typename ... Args>
void my_func(Args&& ... args, const my_special_types& x);

Делая что-то вроде:

template<typename... Args>
void my_func(Args&& ... args)
    -> call my_func_reversed(args...)
template<typename... RevArgs>
void my_func_reversed(const my_special_types& x, RevArgs&&... revargs)
    -> do separate things with revargs and my_special_types
    -> sub_func_reversed(revargs...)

Использование вышеупомянутых утилит.

Есть некоторые (многие) недостатки. Перечислим их ниже.

Scope

Это для пользователей C ++ 14 (возможно, C ++ 11), которые хотят заимствовать из будущего (C ++ 17) .

Шаг 1: Обратные аргументы

Существует несколько способов сделать это. Я привел некоторые альтернативы в этом примере:

  • tuple.cc - игровая площадка для двух альтернатив (кредиты в исходном коде): используйте складные выражения и манипулируйте индекс прошел через std::apply_impl (кредит: Orient). Используйте рекурсивные шаблоны для построения обратного index_sequence (credit: Xeo)
  • tuple.output.txt - Пример вывода. Выводит шаблон reversed_index_sequence из примера Xeo. Мне это нужно для отладки.
    >>> name_trait<std::make_index_sequence<5>>::name()
    std::index_sequence<0, 1, 2, 3, 4>
    >>> name_trait<make_reversed_index_sequence<5>>::name()
    std::index_sequence<4, 3, 2, 1, 0>
    

Я выбрал вариант 1, так как мне легче переваривать. Затем я попытался быстро его формализовать:

  • tuple_future.h - Заимствование из будущего (namespace stdfuture) и создание расширения (namespace stdcustom)
  • tuple_future_main.cc - Простые, расширенные и полезные примеры (см. ниже) с использованием выше
  • tuple_future_main.output.txt - Пример вывода

Определение фрагментов (адаптация C ++ 17 возможной реализации std::apply на cppreference.com):

namespace detail {
template <class F, class Tuple, std::size_t... I>
constexpr decltype(auto) apply_reversed_impl(F &&f,
    Tuple &&t, std::index_sequence<I...>) 
{
    // @ref https://stackoverflow.com/a/31044718/7829525
    // Credit: Orient
    constexpr std::size_t back_index = sizeof...(I) - 1;
    return f(std::get<back_index - I>(std::forward<Tuple>(t))...);
}
} // namespace detail
template <class F, class Tuple>
constexpr decltype(auto) apply_reversed(F &&f, Tuple &&t) 
{
    // Pass sequence by value to permit template inference
    // to parse indices as parameter pack
    return detail::apply_reversed_impl(
        std::forward<F>(f), std::forward<Tuple>(t),
        std::make_index_sequence<
            std::tuple_size<std::decay_t<Tuple>>::value>{});
}

Использование фрагментов: (из tuple_future_main.output.txt, скопирован сверху)

auto my_func_callable = [] (auto&& ... args) {
    return my_func(std::forward<decltype(args)>(args)...);
};
auto my_func_reversed =
    stdcustom::make_callable_reversed(my_func_callable);

Шаг 2: Застегните свою обувь (с помощью патчей с измененными пачками)

Сначала установите шаблоны для окончательных аргументов, которые вы хотите использовать. Вам нужно будет явно перечислить их, поскольку у вас может быть только один пакет параметров.

(Взято из tuple_future_main.cc ):

Пример сценария:

Нам нравится добавлять вещи в контейнеры с именем, что-то вроде формы:

add_item(const Item& item, const string& name, Container& c)

Мы также можем создать элемент с [ужасно большим] числом перегрузок, и мы есть

add_item(${ITEM_CTOR_ARGS}, const string& name, Container& c)

Чтобы сделать это, мы можем объявить следующее:

void add_item_direct(const Item& item, const string& name, Container& c)
Item create_item(Args&&... args)

И затем определить наши общие интерфейсы:

template<typename... Args>
void add_item(Args&&... args) {
    ...
    auto reversed = stdcustom::make_callable_reversed(callable);
    reversed(std::forward<Args>(args)...);
}
template<typename ... RevArgs>
void add_item_reversed(Container& c, const string& name, RevArgs&&... revargs)
{
    ...
    static auto ctor = VARIADIC_CALLABLE(create_item,);
    ...
    auto item = ctor_reversed(std::forward<RevArgs>(revargs)...);
    add_item_direct(item, name, c);
}

Теперь мы можем делать такие вещи, как: (взято из tuple_future_main.output.txt)

>>> (add_item(Item("attribute", 12), "bob", c));
>>> (add_item("attribute", 12, "bob", c));
>>> (add_item(Item(2, 2.5, "twelve"), "george", c));
>>> (add_item(2, 2.5, "twelve", "george", c));
>>> (add_item(Item(2, 15.), "again", c));
>>> (add_item(2, 15., "again", c));
>>> c
bob - ctor3: ctor3: ctor1: attribute (12, 10)
bob - ctor3: ctor1: attribute (12, 10)
george - ctor3: ctor3: ctor2: 2, 2.5 (twelve)
george - ctor3: ctor2: 2, 2.5 (twelve)
again - ctor3: ctor3: ctor2: 2, 15 ()
again - ctor3: ctor2: 2, 15 ()

Обратите внимание на дополнительные конструкторы копирования ...: (

Недостатки

  • Ужасно, как черт
  • Не может быть полезным. Просто проще реорганизовать ваши интерфейсы. Однако это можно использовать в качестве стоп-зазора для перехода к более обобщенному интерфейсу. Возможно, меньше строк для удаления Особенно если он подключает ваш процесс разработки с использованием шаблонных взрывов
  • Невозможно пригвоздить место, где появляются дополнительные копии fr ом. Это может быть из-за разумного использования вариабельных лямбдов
  • . Вы должны тщательно выполнять свою базовую функциональность. Вы не должны пытаться расширить существующую функцию. Пакеты параметров будут жадными в том, как они соответствуют функциям. Вам либо нужно явно указать каждую перегрузку, которую вы хотите, либо поклониться, и пусть пакет параметров вариационных параметров отправится в нужную вам функциональность. Если вы найдете элегантный способ обойти это, пожалуйста, дайте мне знать ,
  • Ошибки шаблона - дерьмо. Конечно, не слишком дерьмовый. Но трудно сделать вывод, что вы пропустили доступную перегрузку.
  • Обтекает множество простых функций в lambdas. Вы можете использовать make_reversed_index_sequence и напрямую отправлять функции (упомянутые в других сообщениях SO). Но это больно повторять.

Todo

  • Избавиться от дополнительных копий
  • Свести к минимуму необходимость всех лямбда Не обязательно, если у вас есть Callable
  • Попробуйте бороться с жадностью пакета параметров. Существует ли обобщенное соответствие std::enable_if, которое соответствует как lvalue, так и rvalue-ссылкам, и, возможно, обрабатывать совместимые неявные конструкторы копирования?
    template<typename ... Args>
    void my_func(Args&& ... args) // Greedy
    void my_func(magical_ref_match<string>::type, ...)
        // If this could somehow automatically snatch `const T&` and `T&&` from the parameter pack...
        // And if it can be used flexible with multiple arguments, combinatorically
    

Надежды

  • Возможно, C ++ 17 будет поддерживать не финальные аргументы пакета параметров, так что все это можно отбросить ... скрещены пальцы
1
ответ дан Community 25 August 2018 в 07:59
поделиться

Из рабочего проекта стандарта N3376 § 14.1 является вероятным разделом, чтобы прочитать об этом.

Ниже приведено § 14.1.11

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

0
ответ дан Lightness Races in Orbit 25 August 2018 в 07:59
поделиться

Итак, должен быть обходной путь. Что-то в этих строках:

namespace v1 {
  // Extract the last type in a parameter pack.
  // 0, the empty pack has no last type (only called if 1 and 2+ don't match)
  template<typename... Ts>
  struct last_type {};

  // 2+ in pack, recurse:
  template<typename T0, typename T1, typename... Ts>
  struct last_type<T0, T1, Ts...>:last_type<T1, Ts...>{};

  // Length 1, last type is only type:
  template<typename T0>
  struct last_type<T0> {
    typedef T0 type;
  };
}
namespace v2 {
  template<class T> struct tag_t{using type=T;};
  template<class T> using type_t = typename T::type;
  template<class...Ts>
  using last = type_t< std::tuple_element_t< sizeof...(Ts)-1, std::tuple<tag_t<Ts>...> > >;
  template<class...Ts>
  struct last_type {
    using type=last<Ts...>;
  };
}
template<class...Ts>
using last_type=v2::late_type<Ts...>; // or v1   


struct S
{
    // We accept any number of arguments
    // So long as the type of the last argument is an int
    // probably needs some std::decay to work right (ie, to implicitly work out that
    // the last argument is an int, and not a const int& or whatever)
    template <typename... T, typename=typename std::enable_if<std::is_same<int, typename last_type<T...>::type>>::type>
    S(T...);

};

, где мы проверяем, что последний тип пакета параметров является int, или что мы только передали int.

6
ответ дан Yakk - Adam Nevraumont 25 August 2018 в 07:59
поделиться
Другие вопросы по тегам:

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