【问题标题】:C++: candidate template ignored when iterating over tupleC ++:迭代元组时忽略候选模板
【发布时间】:2021-07-08 10:12:10
【问题描述】:

我正在尝试使用以下代码遍历一个元组:

template <std::size_t I = 0, typename... Ts>
    requires (I >= sizeof...(Ts))
static inline auto consume_all(std::tuple<Ts...>&&, auto) -> void {}

template <std::size_t I = 0, typename... Ts>
    requires (I < sizeof...(Ts))
static inline auto consume_all(std::tuple<Ts...>&& tup, auto f) -> void {
    f(std::get<I>(tup));
    consume_all<I + 1, Ts...>(tup, f);
}

据我所知,上述定义没有任何问题。但是,当我尝试调用它时...:

template <typename F, typename T>
concept unary = requires(F&& f, T&& t) {
    {
        f(t)
    } -> std::convertible_to<T>;
};

//...
//in struct definition:

    const std::tuple<Fs...> funcs;

//...
//in method definition:

                consume_all<Fs...>(
                        funcs,
                        [=, &t]<unary<T> F>(F f) {t = f(t);}
                );

...我收到一个编译时错误,说两个模板都被忽略了:

No matching function for call to 'consume_all' in instantiation of member function 'chain<int, [function-typenames]>'
...
candidate template ignored: invalid explicitly-specified argument for template parameter 'I'
candidate template ignored: invalid explicitly-specified argument for template parameter 'I'

我看不出这个问题,因为... &gt;= I... &lt; I 的组合应该是包罗万象的。

编辑:

当指定 I (consume_all&lt;0, Fs...&gt;) 时,候选模板仍然会被忽略,尽管现在它们会产生更具描述性的错误:

No matching function for call to 'consume_all'
...
candidate template ignored: //issue
deduced type 'tuple<...>' of 1st parameter does not match adjusted type
'const tuple<...>' of argument
[with I = 0, Ts = <(lambda)>, f:auto = (lambda)]

candidate template ignored: //rightfully
constraints not satisfied
[with I = 0, Ts = <(lambda)>, auto:3 = (lambda)]
because '0U >= sizeof...(Ts)' (0 >= 1) evaluated to false

【问题讨论】:

  • 顺便说一句,在 C++17 中:std::apply([&amp;](auto&amp;&amp;... args){ (f(args), ...); }, tup);.
  • std::tuple&lt;Ts...&gt;&amp;&amp; 不是转发引用,所以你需要consume_all(std::move(funcs), lambda)

标签: c++ templates tuples iteration c++-concepts


【解决方案1】:

在您的电话中

consume_all<Fs...>

你错过了I。你需要

consume_all<0, Fs...>

【讨论】:

  • I 是可选的,由 std::size_t I = 0 指定。默认为零。
  • @Vye I 可能是可选的,但由于它是第一个参数,它将绑定到Fs... 的第一个元素
  • @Alan Birtles 我现在了解到参数包绑定很奇怪。 但是这个答案仍然没有解决问题。
  • 逻辑上的想法是省略所有模板参数。
【解决方案2】:

查看@Jarod42 的评论后,我找到了一种更好的方法来遍历一个回避模板问题的元组。

consume_all 定义为迭代函数不是可行的方法。

应该使用 std::apply(func, tup) 来定义 consume_all,它解析元组 tup 作为 func 的参数。

现在,这将遍历元组的问题转换为遍历参数包的问题(更容易)。

遍历参数包的元素:

template <typename F>
[[maybe_unused]] static inline void consume(F) {}
//^^^ Empty Base Case ^^^

template <typename F, typename Arg>
    requires std::invocable<F, Arg>
[[maybe_unused]] static inline void consume(F f, Arg arg) {
    f(arg);
}
//^^^ Single Arg Base Case ^^^

template <typename F, typename Arg, typename... Args>
    requires std::invocable<F, Arg>
static inline void consume(F f, Arg arg, Args... args) {
    f(arg);
    consume<F, Args...>(f, args...);
}
//^^^ Recursive General Case ^^^

现在我们已经定义了一个函数来使用 parm 包的每个元素,我们现在可以正确定义 consume_all

重新定义 consume_all:

template <typename F, typename... Args>
[[maybe_unused]] static inline void consume_all(F f, std::tuple<Args...> tup) {
    std::apply(
            [&f](auto&&... args){
                consume(f, args...);
            },
            tup
    );
}

解决原来的问题:

        consume_all(
                [&t]<unary<T> F>(F f) {t = f(t);},
                funcs
                );

我希望这可以帮助其他有类似问题的人

【讨论】:

  • 从我的评论来看,甚至你的(递归)consume 也不需要,折叠表达式consume(f, args...); -> (f(args), ...);
  • consume_all的要求:requires (std::invocable&lt;F, Args&gt; &amp;&amp; ...)
猜你喜欢
  • 1970-01-01
  • 2012-09-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-06-26
  • 2012-08-26
  • 1970-01-01
  • 2015-10-12
相关资源
最近更新 更多