我正在尝试使用 Functor、Monoid 等类型类在 C++ 中实现类似 Haskell Prelude 的东西。所以我决定使用 C++ 概念来进行类型检查,但我在尝试实现 Semigroup 概念时遇到了问题。
当我尝试声明一个使用某些函数的概念时出现问题,该函数在代码后面为所需类型声明和重载,但它仅与某些类型相关,如 std::vector,但对于我自己的类型效果很好.
完整代码:
template<class T>
struct S {
// ...
};
template<class T>
concept Addable = requires(T a, T b) {
{ add(a, b) } -> T;
};
template<class T>
S<T> add(const S<T> & a, const S<T> & b) {
// ...
return {};
}
template<class T>
std::vector<T> add(const std::vector<T> & a, const std::vector<T> & b) {
// ...
return {};
}
int main() {
std::cout << Addable<S<int>> << '\n';
std::cout << Addable<std::vector<int>> << '\n';
}
我期望输出 1 1
,但实际输出是 1 0
。
因此,无论声明的 add(std::vector) 重载如何,它都不会将 std::vector 识别为 Addable,但它会将 S 类型识别为 Addable。
更新:
如果我将 add
函数移动到 Addable
概念之前,问题就会消失。但是,在我包含这些概念之前,我必须实现我为 std::vector
等类型包含的所有概念,这看起来非常不便且不清楚。
UPD2: 我使用 GCC 9.1.0。
最佳答案
我认为这是这个实验性 Clang 概念实现中的一个缺陷。 [temp.concept]/8说:
A concept is not instantiated ([temp.spec]).
另请参阅:In which access control context are evaluated concepts?
这意味着在 [temp.res] 中描述的名称解析不适用于作为概念定义的表达式。如果必须实例化概念,则函数 add
无法通过不合格的名称查找找到。这就是 Clang 产生错误的原因。
根据标准,当一个 id-expression 命名一个概念时,normalized constraint-expression 在出现 id-expression ( [expr.prim.id]/4 ) 的地方计算。
通俗地说,作为概念定义的表达式是在命名概念的表达式的上下文中求值的。所以Addable<vector<int>>
应该是真的,因为在这个表达式的上下文中,add
可以通过非限定名称查找找到。
关于C++ 概念看到我的类型的函数,但看不到 std::vector 的函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56509883/