为什么 && 在编译时严格?

Why is && strict in compile time?

我正在试验基本的模板元编程。我尝试实现结构模板,帮助我们确定它们的模板参数是否为质数。即:

template<int N, int D>
struct IsPrime_Descend {
    const static bool val = (N % D != 0) && IsPrime_Descend<N, D - 1>::val;
};

template<int N>
struct IsPrime_Descend<N, 1> {
    const static bool val = true;
};

template <int N>
struct IsPrime {
    const static bool val = IsPrime_Descend<N, N - 1>::val; 
};

但是上面的实现需要线性时间。我想将其提高到 O(sqrt(n))。当然,引入一个计算平方根的结构模板并从中降维,还有很长的路要走:

template<int N, int D>
struct Sqrt_Descend {
    const static int val = D * D > N ? Sqrt_Descend<N, D - 1>::val : D;
};

template<int N>
struct Sqrt_Descend<N, 1> {
    const static int val = 1;
};

template<int N>
struct Sqrt {
    const static int val = Sqrt_Descend<N, N>::val;
};

template<int N, int D>
struct IsPrime_Descend {
    const static bool val = (N % D != 0) && IsPrime_Descend<N, D - 1>::val;
};

template<int N>
struct IsPrime_Descend<N, 1> {
    const static bool val = true;
};

template <int N>
struct IsPrime {
    const static bool val = IsPrime_Descend<N, Sqrt<N>::val>::val;
};

但我还尝试了其他方法:

template <int N, int D>
struct IsPrime_Ascend {
    const static bool val = (N % D != 0) && (D * D <= N) && IsPrime_Ascend<N, D + 1>::val;
};

template <int N>
struct IsPrime {
    const static bool val = IsPrime_Ascend<N, 1>::val; 
};

由于 &&[=30 的 懒惰,我认为只要前面两个条件 ((N % D != 0) && (D * D <= N)) 为真,此代码段就会实例化 IsPrime_Ascend<N, D> =].但是,显然,当其中之一中断并超过模板实例化最大深度时,它不会停止。

那么,为什么 && 在编译时是严格的(如 不是惰性 )?

Short-circuit 求值处理表达式的 求值。该表达式在 C++ 文件的文本中仍然存在,因此必须对其进行编译。如果该表达式包含模板实例化,则该模板 必须 实例化。这就是编译的工作方式(除非你使用 if constexpr,你不能在那个上下文中使用)。

如果你想阻止进一步的实例化,你必须通过模板规则来实现,而不是表达式评估规则。因此,您需要使用模板的部分专业化,它可能使用在条件为真时处于活动状态的 SFINAE 技术。 C++20 使用 requires 子句使这更容易。

更好的是,将 IsPrime_Descend 变成 constexpr 函数。