Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Concept constrained member function having dependent argument types

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.

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

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

>Solution :

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.

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading