【问题标题】:How to implement folding with variadic templates如何使用可变参数模板实现折叠
【发布时间】:2011-05-19 23:05:50
【问题描述】:

我有一个几乎可行的解决方案。但是,它无法编译一些简单的案例,并且我无法破译错误消息。

我目前的解决方案:

#define AUTO_RETURN( EXPR ) -> decltype( EXPR ) \
{ return EXPR; }

template< typename BinaryFunc, typename First, typename Second >
auto foldl( BinaryFunc&& func, First&& first, Second&& second )
AUTO_RETURN( func( std::forward<First>(first), std::forward<Second>(second) ) )

template<typename BinaryFunc, typename First, typename Second, typename... Rest >
auto foldl( BinaryFunc&& func, First&& first, Second&& second, Rest&&... rest )
AUTO_RETURN(
   foldl(
      std::forward<BinaryFunc>(func),
      func( std::forward<First>(first), std::forward<Second>(second) ),
      std::forward<Rest>(rest)... )
   )

这个works as expected

struct adder
{
   template< int LHS, int RHS >
   std::integral_constant<int,LHS+RHS>
   operator()( std::integral_constant<int,LHS>, std::integral_constant<int,RHS> )
   {
      return {};
   }
};

auto result = foldl( adder(),
      std::integral_constant<int,19>{},
      std::integral_constant<int,23>{}
   );

assert( result.value == 42 );

不过这个fails to compile

foldl( adder(),
      std::integral_constant<int,1>{},
      std::integral_constant<int,2>{},
      std::integral_constant<int,3>{},
      std::integral_constant<int,4>{},
   );

奇怪的是,如果我从代码中删除所有 std::forward 和右值引用,则它是 works fine

我做错了什么?
这是编译器错误吗?

【问题讨论】:

  • 您在 ideone 的一些代码丢失了#include &lt;cassert&gt;
  • 我认为只有 std::forward&lt;Rest&gt;(rest)... 失败了,其余的看起来都很好。
  • @ildjarn,为什么std::forward&lt;Rest&gt;(rest)... 会失败?
  • 这让我想起了stackoverflow.com/questions/3744400/… 这可能是同一个问题

标签: c++ templates c++11 variadic-templates


【解决方案1】:

问题是可变参数foldl的返回类型中的decltype看不到可变参数foldl,因为它还没有完全声明,所以它不能递归到它自己的另一个实例化。

您可以使用助手struct

template<typename BinaryFunc, typename... Args >
struct folder;

template<typename BinaryFunc, typename First, typename Second>
struct folder<BinaryFunc,First,Second>
{
    static auto foldl( BinaryFunc&& func, First&& first, Second&& second )
        AUTO_RETURN( func( std::forward<First>(first), std::forward<Second>(second) ) )
};

template<typename BinaryFunc, typename First, typename Second, typename... Rest >
struct folder<BinaryFunc,First,Second,Rest...>
{
    static auto foldl(BinaryFunc&& func, First&& first, Second&& second, Rest&&... rest )
        AUTO_RETURN(
            (folder<
            BinaryFunc,
             decltype(func( std::forward<First>(first), std::forward<Second>(second) )),Rest...>::
            foldl(
                std::forward<BinaryFunc>(func),
                func( std::forward<First>(first), std::forward<Second>(second) ),
                std::forward<Rest>(rest)... )
                ))
};

template< typename BinaryFunc, typename... Args >
auto foldl( BinaryFunc&& func, Args&& ... args )
AUTO_RETURN(
    (folder<BinaryFunc,Args...>::foldl(
      std::forward<BinaryFunc>(func),
      std::forward<Args>(args)... ))
   )

【讨论】:

  • 那么,这不是编译器错误,而是 C++11 标准中的真正限制吗?你能告诉在标准的文本中哪里可以找到它吗?
  • 我相信是的。我找不到任何地方说被声明的函数的名称 is 在尾随返回类型的范围内,所以默认情况下我会说它 isn't 在范围内在此刻。见 3.3.2p1 --- 函数的声明符包括尾随返回类型。
  • 为什么要添加使用Third 模板参数,而不是FirstSecondRest
  • 这是我最初的代码遗留下来的。这是不必要的;我已将其从答案中删除。
【解决方案2】:

感谢您提出的一个很好的问题,让我研究了 C++11 中的一些新领域。

Anthony W. 的解决方案更快;但我仍然想分享我的,它也使用了一个辅助结构(而且它确实更冗长)。首先,让我分享一个类似问题的链接trailing return type using decltype with a variadic template function;那里的答案基于相同的辅助结构思想,因此他们的作者值得称赞。

下面的代码是checked with ideone。请注意,它不再使用 AUTO_RETURN,因为帮助器结构现在负责类型。

template< typename BinaryFunc, typename First, typename... Types >
struct helper;

template< typename BinaryFunc, typename First>
struct helper<BinaryFunc, First> {
    typedef decltype(std::declval<First>()) type;
};

template< typename BinaryFunc, typename First, typename Second >
struct helper<BinaryFunc, First, Second> {
    typedef decltype(
        std::declval<BinaryFunc>()( std::declval<First>(), std::declval<Second>() )
    ) type;
};

template< typename BinaryFunc, typename First, typename Second, typename... Rest >
struct helper<BinaryFunc, First, Second, Rest...>  {
    typedef typename helper< BinaryFunc,
                             typename helper<BinaryFunc, First, Second>::type,
                             Rest...
                           >::type
                     type;
};

template< typename BinaryFunc, typename First, typename Second >
typename helper<BinaryFunc, First, Second>::type
foldl( BinaryFunc&& func, First&& first, Second&& second ) {
    return func( std::forward<First>(first), std::forward<Second>(second) );
}

template< typename BinaryFunc, typename First, typename Second, typename... Rest >
typename helper<BinaryFunc, First, Second, Rest...>::type
foldl( BinaryFunc&& func, First&& first, Second&& second, Rest&&... rest ) {
   return foldl(
      std::forward<BinaryFunc>(func),
      func( std::forward<First>(first), std::forward<Second>(second) ),
      std::forward<Rest>(rest)...
   );
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-01-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多