Search code examples
c++variadic-templatesc++-concepts

C++ concepts with variadic templates


I've returned to c++ after many years and trying to grasp the concepts. I've written a simple test code, but I'm getting an error which I don't really understand why it is here.

template <typename T, typename U>
concept addable = requires (T a, U b)
{
    a + b;
};

template <typename T>
T test_add(T&& value)
{
    return value;
}

template <typename T, typename... Args>
auto test_add(T&& value, Args&&... rest) requires addable<T, decltype(test_add(forward<Args>(rest)...))>
{
    return value + test_add(forward<Args>(rest)...);
}

The goal is to check if there is a valid + operation on the returning expression, but when i call the function like this:

auto result = test_add(1, 3, 5);

I get following error: "no instance of overloaded function "test_add" matches the argument list".

What is wrong here?


Solution

  • It looks like you want the concept to be variadic, so you need a parameter pack and then a fold expression over the + operator:

    template <class... Ts>
    concept addable = requires(Ts&&... ts) { (... + ts); };
    //                                        ^^^^^^^^
    //                                        fold over +           
    

    Your test_add function templates could then be replaced with one that uses the concept (with a similar fold expression):

    auto test_add(addable auto&&... values) {
        return (... + std::forward<decltype(values)>(values));
    }
    

    If you want two separate function templates, you can (with the same concept as above). You mainly need to change what you put in as a requirement:

    template <typename T>
    decltype(auto) test_add(T&& value) {
        return std::forward<T>(value);
    }
    
    template <typename T, typename... Args>
    auto test_add(T&& value, Args&&... rest)
        requires addable<T, Args...>
    //           ^^^^^^^^^^^^^^^^^^^
    {
        return std::forward<T>(value) + test_add(std::forward<Args>(rest)...);
    }