强制执行所有的 && 运算符吗?

8
考虑下面这个可变参数函数:
template <typename Type, typename... Types>
bool f(Type& arg, Types&... args)
{
    return f(arg) && f(args...);
}

template <typename Type>
bool f(Type& arg)
{
    // Do something
} 

如果递归的某个级别为false,那么我怀疑接下来的内容将不会被执行。是否有一种方法可以强制对所有参数执行递归,即使其中一个返回false


1
& 替换 && 怎么样? - fredoverflow
5个回答

21

这不应该太难:

template <typename Type, typename... Types>
bool f(Type& arg, Types&... args)
{
    bool b1 = f(arg);
    bool b2 = f(args...);
    return b1 && b2;
}

我认为直接删除第二个对 f 的调用即可。 - Joseph Mansfield
为什么需要 f(arg)?难道不应该是 bool b1 = arg 吗? - einpoklum

10

随着辩论在比较AndyProwl和Alon解决方案时的演变,我对两种解决方案进行了基准测试,结果...取决于参数的数量。

编译方式:

g++-4.7 -std=c++11 -Wall -Wextra -O3 main.cpp -o main -D_FIRST

对AndyProwl的解决方案进行基准测试,并使用以下编译:

g++-4.7 -std=c++11 -Wall -Wextra -O3 main.cpp -o main -D_SECOND

对Alon解法进行基准测试。

这是10个参数的基准测试程序。

#include <iostream>
#include <chrono>

// Function 1 : with &&
template <typename Type>
inline bool f1(const Type& arg)
{
   return arg;
}
template <typename Type, typename... Types>
inline bool f1(const Type& arg, const Types&... args)
{
    bool arg1 = f1(arg);
    bool arg2 = f1(args...);
    return arg1 && arg2;
}

// Function 2 : with &
template <typename Type>
inline bool f2(const Type& arg)
{
   return arg;
}
template <typename Type, typename... Types>
inline bool f2(const Type& arg, const Types&... args)
{
    return f2(arg) & f2(args...);
}

// Benchmark
int main(int argc, char* argv[])
{
    // Variables
    static const unsigned long long int primes[10] = {11, 13, 17, 19, 23, 29, 31, 37, 41, 43};
    static const unsigned long long int nbenchs = 50;
    static const unsigned long long int ntests = 10000000;
    unsigned long long int sum = 0;
    double result = 0;
    double mean = 0;
    std::chrono::high_resolution_clock::time_point t0 = std::chrono::high_resolution_clock::now();

    // Loop of benchmarks
    for (unsigned long long int ibench = 0; ibench < nbenchs; ++ibench) {

        // Initialization
        t0 = std::chrono::high_resolution_clock::now();
        sum = 0;

        // Loop of tests
        for (unsigned long long int itest = 1; itest <= ntests; ++itest) {
#ifdef _FIRST
            sum += f1((itest+sum)%primes[0], (itest+sum)%primes[1], (itest+sum)%primes[2], (itest+sum)%primes[3], (itest+sum)%primes[4], (itest+sum)%primes[5], (itest+sum)%primes[6], (itest+sum)%primes[7], (itest+sum)%primes[8], (itest+sum)%primes[9]);
#endif
#ifdef _SECOND
            sum += f2((itest+sum)%primes[0], (itest+sum)%primes[1], (itest+sum)%primes[2], (itest+sum)%primes[3], (itest+sum)%primes[4], (itest+sum)%primes[5], (itest+sum)%primes[6], (itest+sum)%primes[7], (itest+sum)%primes[8], (itest+sum)%primes[9]);
#endif
        }

        // Finalization
        result = std::chrono::duration_cast<std::chrono::duration<double>>(std::chrono::high_resolution_clock::now()-t0).count();
        mean += result;
        std::cout<<"time = "<<result<<" (sum = "<<sum<<")"<<std::endl;
    }

    // End
    std::cout<<"mean time = "<<mean/nbenchs<<std::endl;
    return 0;
}

对于每个给定数量参数的解决方案,我们有50个基准测试,离散度非常小,并且这些基准测试的平均时间是一个可靠的指标。

我的第一个基准测试是使用“正确”数量的参数,其中Alon的解决方案比AndyProwl的解决方案快。

最终结果在此处:

Benchmark

因此,AndyProwl的解决方案通常比Alon的更快。现在我可以验证您的答案。但我认为差异非常小,取决于架构/编译器。

所以:

  • AndyProwl+1 是您一般较快的解决方案
  • Alon+1 是您支持constexpr的解决方案

由于我已经发布了代码,您可以运行它多次并计算离散度。 - Vincent
两种解决方案是可以互换的,因为编译器可以通过“as-if”原则执行其中之一。没有副作用,结果也是相同的。因此,最好的情况是,您的基准测试检测到潜在的优化错误。-1 - einpoklum
实际上,这两种方法得到的代码完全相同,例如对于两个参数的情况。请参见此处 - einpoklum

4
你可以分别执行它们并返回一个布尔表达式:
bool b0 = f(arg);
bool b1 = f(args);
return b0 && b1;

4

没有递归:

template <typename... Types>
bool f(Types&&... args)
{
  bool r=true;
  (void)std::initializer_list<bool>{(r = f(args)&&r)...};
  return r;
}

-2

有一个更好的技巧,而不是在所有函数之间使用 &&,只需使用一个 &

static_cast<bool>(f(arg)) & static_cast<bool>(f2(args)) ... 将运行所有操作,而不管结果 :)


1
这可以通过 static_cast<bool> 来修复。我喜欢这种方法,因为它比使用辅助变量更简洁。 - ipc
1
@Alon:在大多数情况下?这只意味着它会是一个更危险的漏洞。但好吧,你“修复”了它,所以我撤销-1。但它不再是“好看的”(依我之见)。 - Daniel Frey
15
我不会称这为“更好的技巧”。它只会使代码更难阅读、调试和维护。@Vincent:为什么不使用临时变量呢?如果你担心性能问题,那么没有性能惩罚,并且它可以使你的意图清晰。而这种方式并不能达到这个效果。习惯于阅读C++的人们实际上期望在阅读像这样的一段代码时看到短路行为,因为这是通常的行为,而且&对于逻辑“与”而言并不是通常用法。你不必接受我的答案,但请不要从这里学习。 - Andy Prowl
5
以这种方式做不会带来任何性能提升,任何编译器都会自动优化掉临时变量。如果编译器真的很愚蠢,那么我认为这个版本更可能会引入性能损失,因为需要进行到 bool 的转换和按位 & 运算。 - Andy Prowl
1
@AndyProwl:我需要检查汇编代码,但是我刚刚对你的解决方案和这个(没有使用static_cast,因为f返回一个布尔值)进行了4组50个基准测试,结果这个比你的快7.5%(使用g++ 4.7.2和-O3)。 - Vincent
显示剩余8条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接