【问题标题】:Understanding why compile-time boolean algebra doesn't work理解为什么编译时布尔代数不起作用
【发布时间】:2026-02-13 00:05:01
【问题描述】:

我正在尝试实现一个基本的模板元编程结构,它使用std::is_same 确定类型列表是否都相同。我尝试按如下方式实现它:

template <typename T, typename U, typename... Args>
struct check_same {
     static const bool value = std::is_same<T, U>::value && check_same<U, Args...>::value;
};

template <typename T, typename U>
struct check_same {
     static const bool value = std::is_same<T, U>::value;
};

但是,如果我尝试实例化 check_same,则会收到以下编译器错误:

'check_same' : 模板参数太少

为什么这不是执行编译时布尔代数的有效方法?当然,所有涉及的表达式都是constexpr(或const,因为MSVC 还没有实现constexpr),它应该编译吗?


以下将无法编译:

int main() 
{
    static_assert( check_same<int, unsigned int, float>::value, "Types must be the same" );

    return 0;
}

【问题讨论】:

标签: c++ templates c++11 template-meta-programming


【解决方案1】:

C++ 中的类模板不能像函数一样“重载”。您不能用一组不同的参数一次又一次地重新声明同一个模板并期望它能够编译。例如,这不会编译

template <typename A> struct S {};
template <typename A, typename B> struct S {};

它不会编译,因为它尝试声明模板类S 两次。这是非法的。

您的代码遇到了完全相同的错误:您已经声明了模板check_same 两次。你不能那样做。

您显然尝试使用的技术应该基于模板专业化,而不是基于模板重新声明。您只需声明一次模板

template <typename T, typename U, typename... Args>
struct check_same {
     static const bool value = std::is_same<T, U>::value && check_same<U, Args...>::value;
};

然后为特定的、更受限制的参数集提供该主模板的部分特化

template <typename T, typename U>
struct check_same<T, U> {
     static const bool value = std::is_same<T, U>::value;
};

【讨论】:

    【解决方案2】:

    试试这个:

    template <typename T, typename... Args>
    struct check_same: std::true_type {};
    
    template <typename T, typename U, typename...Args>
    struct check_same<T,U,Args...>: std::integral_constant<bool,
      std::is_same<T,U>::value && check_same<U, Args...>::value
    > {};
    

    第一个仅在只有一个参数时才进行评估。第二个专精捕获 2 个或更多。

    如果您传递 0 个参数,它会出错。我认为你也应该处理这个问题:

    template <typename... Args>
    struct check_same: std::true_type {};
    
    template <typename T, typename U, typename...Args>
    struct check_same<T,U,Args...>: std::integral_constant<bool,
      std::is_same<T,U>::value && check_same<U, Args...>::value
    > {};
    

    实际上不需要更多的专业化,因为第一个适用于 0 和 1 参数。

    【讨论】:

    • @dyp 如何减少实例化的数量?你每次递归都会实例化 3 种类型,和我一样吗?
    • 这是某种短路:当一个检查返回 false 并且不执行其余检查时,递归结束。但是,由于conditional,您每一步都会获得一个实例化,所以这可能不值得。
    • @dyp 条件的两个分支都被完全评估,所以检查被执行,只是被丢弃。要解除短路,您需要比conditional 做更多的工作。
    • +1 用于为此使用std::integral_constant。 loc 太宝贵了,不能浪费它们,不是吗。
    • 嗯?为什么不使用的分支需要实例化? coliru.stacked-crooked.com/a/2c7146fd7e7c8dfe