Search code examples
c++c++20type-traitsc++-concepts

Is there a way to get the type of a dereferenced value from a dereference-able type?


I'm playing around with C++ concepts and came across an interesting problem. I have the following two custom-defined concepts:

template<typename T>
concept is_dereferencable = requires (T t) { *t; };

template<typename T>
concept is_printable = requires (T t) { std::cout << t; };

As the names suggest, the first one is used to determine if a given type can be dereferenced, while the other one to check if a type supports output operator. I also have a function template called println, which looks like this:

template<typename T>
void println(const T& t)
{
    if constexpr (is_dereferencable<T>) {
        if constexpr (is_printable<decltype(*t)>) {
            std::cout << *t << '\n';
        }
    } else if constexpr (is_printable<T>) {
        std::cout << t << '\n';
    }
}

This prints the dereferenced value *t if and only if type T is dereference-able and the type of the dereferenced value is printable. So, for example, I can use this function template with something like an std::optional:

int main
{
    std::optional<std::string> stringOpt {"My Optional String"};
    ::println(stringOpt);

    return 0;
}

This will print My Optional String as expected. While this is nice, the function will just silently print nothing if the type of a dereferenced value of a derefernce-able is not printable. So, for a user-defined type Person, the following will just print nothing:

struct Person
{
    std::string m_name;
    explicit Person(const std::string& name) : m_name {name} {}
};

int main
{
    std::optional<Person> personOpt {"John Doe"}
    ::println(personOpt);
    return 0;
}

So I would like to move the above compile-time ifs to a requires clause itself in order to get compile time errors in such cases. Is there a way to achieve that? Is there a way to get the dereferenced type of a given template type T? To make it a bit clearer, I would like to have something like this:

template<typename T>
requires is_dereferencable<T> && is_printable<decltype(*T)>
void printDereferencable(const T& t)
{
    std::cout << *t << '\n';
}

P.S.: I understand that I could remove the nested if and just fail upon trying to call an output operator on something that doesn't support it. However, I want to specifically move this compile-time error to the concept to get a clearer error message.


Solution

  • another option is put the constraint after parameters, so it has access to them.

    template<typename T>
    void printDereferencable(const T& t)
    requires is_dereferencable<const T&> && is_printable<decltype(*t)>
    {
        std::cout << *t << '\n';
    }
    

    note: it should test on const T&, not T