I have just stumbled over C++20's "concepts", something that I've wanted in the language for years... well kind of.
Consider the code below:
#include <concepts>
#include <iostream>
template <typename T>
concept IFoo = requires(T t) {
{ t.foo() } -> std::convertible_to<int>;
};
class FooImpl
{
public:
int foo() const { return 3; }
};
template <IFoo Foo>
void doSomething(const Foo& foo) {
std::cout << foo.foo() << "\n";
std::cout << foo.bar() << "\n"; // I want this to be an error!
}
int main() {
FooImpl foo;
doSomething(foo); // No error if this is commented out
return 0;
}
I am trying to use the concept IFoo as you would use a normal polymorphic interface (= pure virtual class). The problem that I have is that the call foo.bar() does not produce a compile error, merely a substitution error. This means that the concept IFoo could lie to you, for example when coding like this:
- Write
doSomethingwhere you only callfoo.foo()and create the concept for documentation - Two weeks later: Extend
doSomethingto also callfoo.bar()and forget to update the concept. - Another two weeks later: Write a test for
doSomethingand be dumbfounded when you get a template error even though you completely satisfy the concept in your mock implementation ofIFoo.
Right now, I don't see the advantage of using concepts over simple doc comments:
#include <iostream>
class FooImpl
{
public:
int foo() const { return 3; }
};
/**
* Foo needs to contain the following functions:
*
* - int Foo::foo()
*/
template <typename Foo>
void doSomething(const Foo& foo) {
std::cout << foo.foo() << "\n";
std::cout << foo.bar() << "\n"; // I want this to be an error!
}
int main() {
FooImpl foo;
doSomething(foo); // No error if this is commented out
return 0;
}
Sure, the doc comment can rot, but so can the concept. Is there a way to produce a compile error if you use a function on a concept that is not contained in the concept?
No.
What you want is true generics or "definition checking" as it was called in the C++ proposals. It was included in the C++0x (pre-C++11) concepts, but those were never accepted by the committee.
There are legitimately some C++ factors that make writing concepts more difficult if they were definition checked. For example, the concept in the post allows:
but not necessarily calling on a
constor rvalue:That the result is convertible to an
intmeans that you can use it where anintcan, except you can't: iffoo()directly returns anint, it can be implicitly converted to a type that is implicitly convertible fromint. However, iffoo()returns something convertible toint, it cannot be implicitly converted to this other type.So... as much as I and others would have preferred a "definition checked" version, to write a concept in such a C++ could be even trickier.
So... no, there is not a way to provide that compile error.
Concepts may have other advantages. For example, overloads will be ranked according to "subsumption": an overload taking a more specific concept will be chosen in place of one taking a more generic concept.