C++ concepts see a function for my type, but don't see it for std::vector

Viewed 170

I'm trying to implement something like Haskell Prelude in C++ with typeclasses like Functor, Monoid, and so on. So I've decided to use C++ Concepts to do the type checking, but I encountered a problem while trying to implement Semigroup concept.

The problem occurs when I try to declare a concept that uses some function that is declared and overloaded for needed types later in the code, but it's only relevant for some types like std::vector, but for my own types works well.

Full code:

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';
}

I expect the output 1 1, but the actual output is 1 0. So, it doesn't recognize std::vector as Addable regardless to a declared add(std::vector) overload, but it does recognize the S type as Addable.

UPD: The problem disappears if I move the add function to go before the Addable concept. But it looks very unhandy and unclear that I have to implement all concepts that I include for types like std::vector before I include those concepts.

UPD2: I use GCC 9.1.0.

1 Answers

I think this is a defect in this experimental Clang concept implementation. [temp.concept]/8 says:

A concept is not instantiated ([temp.spec]).

See also: In which access control context are evaluated concepts?

That means that name resolution as it is described in [temp.res] does not apply to an expression that is a concept definition. If concepts had to be instantiated, then the function add could not be find by unqualified name look-up. This is why Clang generates an error.

According to the standard, when a id-expression names a concept, the normalized constraint-expression is evaluated where appears the id-expression ([expr.prim.id]/4).

Less formaly, the expression that is the definition of the concept is evaluated in the context of the expression that names the concept. So Addable<vector<int>> should be true, because in the context of this expression, add can be found by unqualified name look-up.

Related