C++20概念匹配STL流出现意外结果,求技术解答
核心问题:概念定义的逻辑错误
你定义RawInputStream时误用了requires块:
template <typename _StreamT> concept RawInputStream = requires { SameAsStdBasicInputStream<_StreamT> || DerivedFromStdInputStream<_StreamT>; };
requires块的作用是检查其中的表达式是否可合法编译,而非判断表达式的值是否为true。对于任意类型_StreamT,SameAsStdBasicInputStream<_StreamT>和DerivedFromStdInputStream<_StreamT>都是合法的布尔常量表达式,因此它们的逻辑或表达式永远合法,导致RawInputStream对所有类型都返回true——这就是static_assert(RawInputStream<std::ostream>);通过的原因。
正确的概念定义方式
要让RawInputStream真正检查两个子概念是否有一个满足,应该直接使用概念的逻辑组合,不需要requires块:
template <typename _StreamT> concept RawInputStream = SameAsStdBasicInputStream<_StreamT> || DerivedFromStdInputStream<_StreamT>;
修改后,RawInputStream<std::ostream>会因为两个子概念都不满足而触发断言失败,符合你的预期。
关于断言4的结果解释
DerivedFromStdInputStream<std::istream>等价于std::derived_from<std::istream, std::basic_istream<char>>,而std::istream就是std::basic_istream<char>的别名,因此实际是检查std::derived_from<std::istream, std::istream>。
根据C++标准,std::derived_from<T, T>的结果是false——该概念要求派生类与基类不是同一类型(忽略cv限定)。如果需要包含自身的情况,可以改用std::is_base_of_v,或者结合std::same_as:
template <typename _StreamT> concept DerivedFromOrSameAsStdInputStream = std::same_as<_StreamT, std::basic_istream<RawStreamCharType<_StreamT>>> || std::derived_from<_StreamT, std::basic_istream<RawStreamCharType<_StreamT>>>;
替换类型 trait 无变化的原因
你尝试将std::same_as替换为std::is_same_v、std::derived_from替换为std::is_base_of,但结果没有变化——因为这些替换只是改变了表达式的值,但表达式本身依然是合法的,requires块仍然会判定为满足,所以RawInputStream还是对所有类型生效。
修正后的完整代码
#include <iostream> #include <concepts> template <typename T> struct RawStreamTraits { using Ch = typename T::char_type; }; template <typename T> using RawStreamCharType = typename RawStreamTraits<T>::Ch; template <typename _StreamT> concept SameAsStdBasicInputStream = std::same_as<_StreamT, std::basic_istream<RawStreamCharType<_StreamT>>>; template <typename _StreamT> concept DerivedFromStdInputStream = std::derived_from<_StreamT, std::basic_istream<RawStreamCharType<_StreamT>>>; // 修正后的概念定义 template <typename _StreamT> concept RawInputStream = SameAsStdBasicInputStream<_StreamT> || DerivedFromStdInputStream<_StreamT>; static_assert(RawInputStream<std::istream>); // 1: 通过 static_assert(!RawInputStream<std::ostream>); // 2: 现在符合预期,需加! static_assert(SameAsStdBasicInputStream<std::istream>); // 3: 通过 static_assert(!DerivedFromStdInputStream<std::istream>); // 4: 通过(符合预期的false) static_assert(!SameAsStdBasicInputStream<std::ostream>); // 5: 通过(符合预期的false) static_assert(!DerivedFromStdInputStream<std::ostream>); // 6: 通过(符合预期的false)
内容的提问来源于stack exchange,提问作者Stephen Zhang

