Adrian Jałoszewski Adrian Jałoszewski - 3 months ago 21
C++ Question

Specializing templates based on methods

Recently I've been programming a lot in Java, now I'm coming back to my C++ roots (I really started missing the pointers and segmentation faults). Knowing that C++ has a broad support for templates I was wondering whether it has some capabilities of Java which could be useful for writing generalized code. Lets say I'have two groups of classes. One of them has the

first()
method, the other one has the
second()
method. Is there a way of specializing the templates to be picked by the compiler depending on the methods one class possesses? I'm aiming at behavior which is similar to the one of Java:

public class Main {
public static void main(String[] args) {
First first = () -> System.out.println("first");
Second second = () -> System.out.println("second");
method(first);
method(second);
}

static <T extends First> void method(T argument) {
argument.first();
}

static <T extends Second> void method(T argument) {
argument.second();
}
}


Where
First
and
Second
are interfaces. I know I could group both of these groups by deriving each of them from an upper class, but it's not always possible (no autoboxing in C++ and some classes don't inherit from a common ancestor).

A good example of my needs is the STL library, where some classes have methods like
push()
and some others have
insert()
or
push_back()
. Lets say I want to create an function which has to insert multiple values into an container using an variadic function. In Java it's easy to perform because collections have a common ancestor. In C++ on the other hand it's not always the case. I tried it by duck-typing, but the compiler yields an error message:

template <typename T>
void generic_fcn(T argument) {
argument.first();
}

template <typename T>
void generic_fcn(T argument) {
argument.second();
}


So my question is: Is implementing such behavior possible without creating unnecessary boileplate code by specializing every single case?

Answer

Instead of <T extends First>, you will use something we call sfinae. This is a technique about adding constaints on a function based on parameter types.

Here's how you'd do it in c++:

template <typename T>
auto generic_fcn(T argument) -> void_t<decltype(argument.first())> {
    argument.first();
}

template <typename T>
auto generic_fcn(T argument) -> void_t<decltype(argument.second())> {
    argument.second();
}

For the function to exist, the compiler will need the type of argument.second(). If the type doesn't exist, the compiler will try other function.

void_t is implemented as follow:

template<typename...>
using void_t = void;