Search code examples
c++c++20metaprogrammingc++-concepts

template method in C++ concept


I asked a very similar question in template method in a C++ 20 'concepts', and the reason I am asking again is because an AI service said it is part of the C++ standard, although gcc (11.4.0) and clang (14.0.0) do not compile.

This is the code I want to compile, almost identical to the one AI service generated:

#include <concepts>
    
template <typename T>
concept C = requires(T a) {
  { a.m1(std::declval<int>()) } -> std::convertible_to<int>;

  template <std::unsigned_integral U> {
    a.m2(std::declval<int>())
    } -> std::convertible_to<int>;
};

template <C t> void f(t &p_t) { p_t.m1(3); }

class S {
public:
  int m1(int i) { return 2 * i; }
  template <std::unsigned_integral t> int m2(t p_t) { return p_t; }
};

int main() {
  S _s;
  f(_s);
}

The code fails to compile in line template <std::unsigned_integral U> with the report: Expected expression.

So, are template methods in concepts supported in C++20, but not yet implemented; or is my code wrong; or the AI service generated a incorrect code?


Solution

  • You cannot have a template in a requires clause. But your concept can have a second template argument:

    template<typename T, typename U>
    concept C = requires(T a, U in)
    {
        { a.m1(int{}) } -> std::convertible_to<int>;
        std::unsigned_integral<U>; // force U to be unsigned_integral here
        { a.template m2<U>(U{}) } -> std::convertible_to<int>;
    };
    

    Because concepts do not allow for sondtrainment see here, you need to explicitly check that U is a std::unsigned_integral as well.

    Now C has two template arguments, thus your function f needs to be templates over two params as well:

    template<std::unsigned_integral U, C<U> t>
    void f(t& p_t) { p_t.m1(3); } // call with f<unsigned>(_s);
    

    but as you know what kind of unsigned type you have when you call f, this should not be a problem. If you do not need m2 in the function you can decide to provide a default type for U e.g. std::unsigned_integral U = unsigned. But then it might be sensible to take apart your concept, if it is not needed in combination anyways.