c++c++-conceptsargument-dependent-lookup

How to match the int type to a C++ concept requiring a valid function?


I'm trying out templates and concepts in C++. I wrote the following code.

#include <iostream>
#include <string>

template <typename T>
concept Printable = requires(T a) {
    { to_string(a) } -> std::same_as<std::string>;
};

typedef struct myStruct {
    std::string name;
} myStruct_t;

// Implement to_string for myStruct_t
std::string to_string(myStruct_t arg) {
    return arg.name;
}

// Implement to_string for int
std::string to_string(int arg) {
    return "it's an integer";
}

// a function that requires Printable
template <typename T>
requires Printable<T>
void show(T arg) {
    std::cout << to_string(arg) << std::endl;
}

int main(){
    myStruct_t a = {"hello"};
    show(a); // passes compilation
    show(5); // error: no matching function for call to ‘show(int)’
             // note: the required expression ‘to_string(a)’ is invalid
    return 0;
}

The compiler successfully recognizes that myStruct_t satisfies Printable concept but fails to match int with Printable concept. I wonder how I can inform the compiler that the int is also Printable.

I've searched the internet and found that the compiler finds the to_string function needed for Printable concept through argument-dependent lookup(ADL). Since int is not subject to ADL, the compiler fails to recognize the function when trying to check int in Printable. I wonder if there's no way to make int Printable.


Solution

  • Using gcc and -fconcepts-diagnostics-depth=2 we see:

    <source>:216:21:   in requirements with 'T a' [with T = int]
    <source>:217:16: note: the required expression 'to_string(a)' is invalid, because
      217 |     { to_string(a) } -> std::same_as<std::string>;
          |       ~~~~~~~~~^~~
    <source>:217:16: error: 'to_string' was not declared in this scope, and no declarations were found by argument-dependent lookup at the point of instantiation
    <source>:230:13: note: 'std::string to_string(int)' declared here, later in the translation unit
      230 | std::string to_string(int arg) {
          |             ^~~~~~~~~
    

    This means, that the concept is not satisfied for int, because at the point of the concept definition the function to_string(int) was not declared yet.

    I wonder if there's no way to make int Printable

    You can either move the implementation of to_string(int) above the concept or add a forward declaration there.

    // forward-declare to_string for int
    std::string to_string(int arg);
    
    template <typename T>
    concept Printable = requires(T a) {
        { to_string(a) } -> std::same_as<std::string>;
    };
    
    // [...]
    
    // Implement to_string for int
    std::string to_string(int arg) {
        return "it's an integer";
    }