12

我有一个采用可变参数包的函数,一开始我想检查所有元素是否比较相等。我可以以某种方式使用新的 C++17 折叠表达式将其简洁地写成单行吗?我刚在想

template<typename... Args>
void func (Args... args)
{
    ASSERT ((args == ...));

    // more code here...
}

但这不起作用,因为它编译为首先正确比较最后两个参数的代码,然后将倒数第三个参数与第一个比较的结果进行比较,这是一个布尔值。这种类型的折叠表达式可能有什么用例(类似于args < ...)?我有没有机会避免编写专用的递归模板来做到这一点?

4

3 回答 3

28

不幸的是,这不起作用的原因是布尔运算符在 C++ 中不像在其他语言中那样链接。所以表达式:

a == (b == c)

(您的折叠表达式将扩展到什么)将与 or 进行比较atrue与什么或实际上false无关。我希望会添加链接,但显然那部分被删除了。bcoperator<=>

修复是你必须打破比较:

(a == b) && (b == c)

当然,这并不适合折叠,但您可以将所有内容与第一个元素进行比较:

(a == b) && (a == c)

也就是说:

((a0 == args) && ... )

那时,我们只需要能够拉出第一个元素。没问题,这显然是 lambdas 的用途:

template <class... Args>
constexpr bool all_equal(Args const&... args) {
    if constexpr (sizeof...(Args) == 0) {
        return true;
    } else {
        return [](auto const& a0, auto const&... rest){
            return ((a0 == rest) && ...);
        }(args...);
    }
}
于 2017-10-18T12:10:24.507 回答
8

正如 Piotr Skotnicki 所建议的那样,一个简单的解决方案是将第一个参数与以下参数分开,并使用&&as fold 运算符对其进行检查

true例如,如果所有参数都相等,则返回以下函数

template <typename A0, typename ... Args>
bool foo (A0 const & a0, Args const & ... args)
 { return ( (args == a0) && ... && true ); } 

不幸的是,这不适用于空的参数列表

std::cout << foo(1, 1, 1, 1) << std::endl; // print 1
std::cout << foo(1, 1, 2, 1) << std::endl; // print 0
std::cout << foo() << std::endl;           // compilation error

但您可以添加特殊的空参数foo()

bool foo ()
 { return true; }

如果由于某种原因,您不能将argsaa0和以下分开args

嗯……你显然可以使用前面的foo()函数(有特殊的空版本)

template<typename... Args>
void func (Args... args)
{
    ASSERT (foo(args));

    // more code here...
}

或者您可以使用带有逗号运算符和赋值的 C++17 折叠表达式,如下所示bar()

template <typename ... Args>
bool bar (Args const & ... args)
 {
   auto a0 = ( (0, ..., args) );
   return ( (args == a0) && ... && true ); 
 }

观察a0分配中的初始零,它允许使用这个解决方案,也可以使用一个空的参数列表。

不幸的是,从前面的auto a0分配中,我得到了很多我不知道如何避免的警告(“表达式结果未使用”,来自 clang++,和“逗号运算符的左操作数无效”,来自 g++)。

以下是一个完整的工作示例

#include <iostream>

template <typename A0, typename ... Args>
bool foo (A0 const & a0, Args const & ... args)
 { return ( (args == a0) && ... && true ); }

bool foo ()
 { return true; }

template <typename ... Args>
bool bar (Args const & ... args)
 {
   auto a0 = ( (0, ..., args) );
   return ( (args == a0) && ... && true ); 
 }

int main ()
 {
   std::cout << foo(1, 1, 1, 1) << std::endl; // print 1
   std::cout << foo(1, 1, 2, 1) << std::endl; // print 0
   std::cout << foo() << std::endl;           // print 1 (compilation error
                                              //          witout no argument
                                              //          version)

   std::cout << bar(1, 1, 1, 1) << std::endl; // print 1
   std::cout << bar(1, 1, 2, 1) << std::endl; // print 0
   std::cout << bar() << std::endl;           // print 1 (no special version)
 }

- 编辑 -

正如 dfri 所指出的(谢谢!),对于和空args...包,以下折叠表达式的值

( (args == a0) && ... )

( (args == a0) || ... )

分别是truefalse

foo()所以and的返回指令bar()可以写成

 return ( (args == a0) && ... && true );

或者

 return ( (args == a0) && ... );

万一也是如此sizeof...(args) == 0U

但我倾向于忘记这种细节,更喜欢显式(使用 final && true)空值。

于 2017-10-18T10:17:36.963 回答
0

这是我在gcl库中的操作方式:

template <auto ... values>
constexpr static auto equal_v = []() consteval {
    static_assert(sizeof...(values) > 0, "gcl::mp::value_traits::equal_v : no arguments");
    constexpr auto first_value = std::get<0>(std::tuple{values...});
    static_assert(
            (std::equality_comparable_with<decltype(values), decltype(first_value)> && ...),
            "gcl::mp::value_traits::equal_v : cannot compare values");
    return ((values == first_value) && ...); 
}();

或替换static_assert为概念要求:

template <typename ... Ts>
concept are_equality_comparable = requires(Ts ... values)
{
    { 
        std::conditional_t<(std::equality_comparable_with<decltype(std::get<0>(std::tuple{values...})), decltype(values)> && ...), std::true_type, std::false_type>{}
    } -> std::same_as<std::true_type>;
};

template <auto ... values>
    requires(are_equality_comparable<decltype(values)...>)
constexpr static auto equal_v = []() consteval {
    static_assert(sizeof...(values) > 0, "gcl::mp::value_traits::equal_v : no arguments");
    constexpr auto first_value = std::get<0>(std::tuple{values...});
   
    return ((values == first_value) && ...); 
}();
于 2021-01-30T15:14:12.830 回答