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

Why do C++20 concepts (requirement expression) using std::is_arithmetic_v fail to enforce type constraints?


I encountered unexpected behavior while implementing C++20 concepts based on an example from "Template Metaprogramming with C++: Learn everything about C++ templates and unlock the power of template metaprogramming" (by Marius Bancila, 2022).

The following are two versions of concept definitions, which are expected to be equivalent:

Version 1:​​

template<typename T>  
concept arithmetic = requires {  
    std::is_arithmetic_v<T>;  
};  

​​Version 2:​​

template<typename T>  
concept arithmetic = std::is_arithmetic_v<T>;  

However, using the first version:

#include <type_traits>
#include <string>
#include <iostream>

template<typename T>
concept arithmetic = requires {
    std::is_arithmetic_v<T>;
};

template <typename T>
requires arithmetic<T>
T add(T const a, T const b) { return a + b; }

int main() {
    add("a", "b");
}

Both Visual Studio 2022 and https://cppinsights.io/ with -std=c++20 give me a compile error:

'+': cannot add two pointers

But using the next version:

#include <type_traits>
#include <string>
#include <iostream>

template<typename T>
concept arithmetic = std::is_arithmetic_v<T>;

template <typename T>
requires arithmetic<T>
T add(T const a, T const b) { return a + b; }

int main() {
    add("a", "b");
}

The same compilers with the same compile flags give me a different error:

'add': no matching overloaded function found

According to the C++ standard, a simple requirement in a requires clause only checks if the expression is valid, not its boolean value.

Why does the requires-based approach fail here? How can I align the concept definition with the book's intended behavior?


Solution

  • The following are two versions of concept definitions, which are expected to be equivalent:

    They are not.

    The first version checks the expression std::is_arithmetic_v is valid for type T. You want:

    template<typename T>
    concept arithmetic = requires {
        requires std::is_arithmetic_v<T>;
    //  ^~~~~~~ 
    };
    

    Then, all three major compilers output similar error messages: https://godbolt.org/z/41conjGrj