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
.