Well. This week I thought about creating static interfaces in C++ with templates. Rust has this with generics and trait bounds. C# kind of has this, I think.

While it’s possible in C++, it’s cumbersome.

Let’s start with some ducks: a Duck, a RoboDuck, and a NotDuck. Duck and RoboDuck can Quack, but NotDuck can’t.

struct Duck
{
        void Quack() const { std::cout << "Quack" << std::endl; }
};

struct RoboDuck
{
        void Quack() const { std::cout << "Robotic quack" << std::endl; }
};

struct NotDuck {};

Instead of enforcing this with a pure virtual interface, we’ll create a series of templates that act as our traits.

namespace DuckTraits
{
template <typename T> struct Quacks : std::false_type {};
template <> struct Quacks<Duck> : std::true_type {};
template <> struct Quacks<RoboDuck> : std::true_type {};

template <typename T> struct DoesntQuack : std::true_type {};
template <> struct DoesntQuack<Duck> : std::false_type {};
template <> struct DoesntQuack<RoboDuck> : std::false_type {};
} /* DuckTraits */

Time to start quacking. We’ll define a free function that tells our ducks to quack.

namespace DuckOps
{
template <typename T>
void DoQuack(T &t, typename std::enable_if<DuckTraits::Quacks<T>::value>::type* = nullptr)
{
        t.Quack();
}

template <typename T>
void DoQuack(T &t, typename std::enable_if<DuckTraits::DoesntQuack<T>::value>::type* = nullptr)
{
        static_assert(DuckTraits::Quacks<T>::value, "Must be able to quack!");
}
} /* DuckOps */

These function templates will only allow things that can quack to compile. That is, this compiles:

% cat main.cpp
int main()
{
        Duck d;
        DuckOps::DoQuack(d);

        RoboDuck rd;
        DuckOps::DoQuack(rd);
}
% c++ -std=c++11 main.cpp
% ./a.out
Quack
Robotic quack

But this doesn’t:

% cat broken.cpp
int main()
{
        NotDuck nd;
        DuckOps::DoQuack(nd);
}
% c++ -std=c++11 broken.cpp
./DuckOps.h:18:2: error: static_assert failed due to requirement 'DuckTraits::Quacks<NotDuck>::value' "Must be able to quack!"
        static_assert(DuckTraits::Quacks<T>::value, "Must be able to quack!");
        ^             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
broken.cpp:10:11: note: in instantiation of function template specialization 'DuckOps::DoQuack<NotDuck>' requested here
        DuckOps::DoQuack(nd);
                 ^
1 error generated.
make: *** [main.o] Error 1

Some closing thoughts

This is not fun. At least the way I implemented is not fun. It’s a lot of boilerplate. It forces you to be extremely explicit. You must enumerate all valid types for a given trait. But, of course, this comes with a benefit: static polymorphism akin to Rust’s trait bounds. C++20 aims to make this easier with constraints and concepts, but it still doesn’t seem as nice as Rust.