Home > database >  Concept constrained member function having dependent argument types
Concept constrained member function having dependent argument types

Time:07-25

I was doing some experiments with concepts, and I was trying to have constrained member functions that must be instantiated only if a concept is satisfied:

template <typename T>
concept Fooable = requires(T o)
{
    o.foo(uint());
};

template <typename T>
concept Barable = requires(T o)
{
    o.bar(uint());
};

class Foo
{
public:
    using FooType = int;
    void foo(uint) {}
};

class Bar
{
public:
    using BarType = double;
    void bar(uint) {}
};

template <typename T>
class C
{
public:
    void fun(typename T::FooType t) requires Fooable<T> {}
    void fun(typename T::BarType t) requires Barable<T> {}
};

int main()
{
    C<Foo> f;
}

This piece of code does not compile both on GCC 11.2 and Clang 14, saying:

main.cpp: error: no type named 'BarType' in 'Foo'
main.cpp: error: no type named 'BarType' in 'Foo'
        void fun(typename T::BarType t) requires Barable<T> {}
                 ~~~~~~~~~~~~^~~~~~~
main.cpp: note: in instantiation of template class 'C<Foo>' requested here
        C<Foo> f;
               ^

However, since I am declaring a C object with Foo type, I expect that the member function fun with BarType would not be instantiated.

Could this be both a GCC and Clang bug? Or am I doing something wrong? Is there any way to implement this using concepts?

CodePudding user response:

Since fun is not a template function, typename T::BarType is always instantiated and produces a hard error if T does not have a type alias named BarType. You might want to do

template <typename T>
class C
{
public:
    template<Fooable U = T>
      requires requires { typename U::FooType; }
    void fun(typename U::FooType t);
    template<Barable U = T>
      requires requires { typename U::BarType; }
    void fun(typename U::BarType t);
};

Given that BarType is associated with the concept Barable, a more appropriate approach would be to redefine your concepts as (I replaced uint() with 0U because there is no so-called uint type in the standard)

template<typename T>
concept Fooable = requires(T o) {
  o.foo(0U);
  typename T::FooType;
};

template<typename T>
concept Barable = requires(T o) {
  o.bar(0U);
  typename T::BarType;
};

template <typename T>
class C {
  public:
    template<Fooable F = T>
    void fun(typename F::FooType t);
    template<Barable B = T>
    void fun(typename B::BarType t);
};

which requires that types satisfying Barable must have a type alias named BarType.

  • Related