c++c++20c++-concepts

Trouble with nested requirements in case of c++ concepts


I am learning about nested requirements in c++ concepts. Consider the below code snippet:

#include <concepts>
#include <iostream>

// Nested requirement
template <typename... Ts>
concept VariadicAddable = requires(Ts... vs)
{
    (... + vs);                         // + operator is provided
    requires sizeof...(Ts) > 1;         // Atleast two arguments are provided
};

template <VariadicAddable... Ts>
auto add_variadic(Ts... vs)
{
    return (... + vs);
}

int main()
{
    std::cout << add_variadic(1, 2) << std::endl;   // Expecting no failure here
    //std::cout << add_variadic(1) << std::endl;    // Expecting failure here
}

I am getting errors when compiling this... (Godbolt, msvc vs17.10 with c++20 option)

<source>(20): error C2672: 'add_variadic': no matching overloaded function found
<source>(13): note: could be 'auto add_variadic(Ts...)'
<source>(20): note: the associated constraints are not satisfied
<source>(12): note: the constraint was not satisfied

I also tried a variation of specifying the same constraint like below:

template <typename... Ts>
concept VariadicAddable = (sizeof...(Ts) > 1) && requires(Ts... vs)
{
    (... + vs);  // + operator is provided
};

Getting similar error in that case as well. Any idea what am I missing?


Solution

  • The issue is that concepts preceding template type parameters should constrain a single type, rather than the relationship between multiple type parameters. Your concept itself is fine, but it cannot be used in the way you have shown.

    I tried to compile with clang and got a slightly clearer error message:

    varadd.cpp:13:6: note: candidate template ignored: constraints not satisfied [with Ts = <int, int>]
       13 | auto add_variadic(Ts... vs)
          |      ^
    varadd.cpp:12:11: note: because 'int' does not satisfy 'VariadicAddable'
       12 | template <VariadicAddable... Ts>
          |           ^
    varadd.cpp:9:14: note: because 'sizeof...(Ts) > 1' (1 > 1) evaluated to false
        9 |     requires sizeof...(Ts) > 1;         // Atleast two arguments are provided
          |              ^
    varadd.cpp:12:11: note: and 'int' does not satisfy 'VariadicAddable'
       12 | template <VariadicAddable... Ts>
          |           ^
    varadd.cpp:9:14: note: because 'sizeof...(Ts) > 1' (1 > 1) evaluated to false
        9 |     requires sizeof...(Ts) > 1;         // Atleast two arguments are provided
    

    It is clear that each int is checked for the concept VariadicAddable individually, instead of having the whole pack passed to the concept. For sizeof...(Ts) to be greater than 1 (say it's 2), the concept before the template parameter would need to be in the form of VariadicAddable<T> for some type T, instead of just VariadicAddable, but that's clearly not your intent.

    The fix is simple: Just use the concept in the requires clause.

    template <typename... Ts>
    requires VariadicAddable<Ts...>
    auto add_variadic(Ts... vs)
    {
        return (... + vs);
    }