Home > database >  How to pass reference of noncopyable type to SFINAE "catch-all" overload function?
How to pass reference of noncopyable type to SFINAE "catch-all" overload function?

Time:08-28

I want to handle noncopyable type by reference when SFINAE get unkown input, my code below can't work, is there a better way?

#include <iostream>
#include <functional>

template<typename T, typename std::enable_if<std::is_integral<T>::value, int>::type = 0>
void data_type(T const& t) {
    std::cout << "integer" << std::endl;
}

void data_type(...) {
    std::cout << "catch unknown" << std::endl;
}

int main() {
    struct noncopyable_type {
        int i;
        noncopyable_type() {}
        noncopyable_type(const noncopyable_type&) = delete;
    };

    int i;
    noncopyable_type s;

    // first try
    data_type(i);   // ok
    data_type(s);   // error: call to deleted constructor

    // try again
    data_type(std::cref(i)); // ok, but the type is std::reference_wrapper, not integer
    data_type(std::cref(s)); // ok
}

CodePudding user response:

There are probably many ways, this is the first one that came to mind. Live demo

#include <iostream>
#include <functional>

template <typename T, 
          typename = typename std::enable_if<std::is_integral<T>::value>::type>
void data_type(T const& t) {
    std::cout << "integer" << std::endl;
}

template <typename ... T, 
          typename = typename std::enable_if<sizeof...(T)==1>::type>
void data_type(T const&...) {
    std::cout << "unknown" << std::endl;
}

int main() {
    struct noncopyable_type {
        noncopyable_type() {}
        noncopyable_type(const noncopyable_type&) = delete;
    };

    int i;
    noncopyable_type s;

    // first try
    data_type(i);   // ok
    data_type(s);   // ok
}

In C 17 I would just use if constexpr.

CodePudding user response:

We rarely need to use the ... trick anymore. With concepts, we can get the overload resolution behaviour we need without having to play tricks with the parameter declaration clause:

template <typename T>
requires std::integral<T>
void data_type(T const& t) {
    std::cout << "integer" << std::endl;
}

template <typename T>
void data_type(T const& t) {
    std::cout << "unknown" << std::endl;
}

The first overload is more constrained than the second one, so the second one will only be used when the first one is not applicable due to its constraint not being satisfied.

Note that the first overload may equivalently be written like so:

template <std::integral T>
void data_type(T const& t) {
    std::cout << "integer" << std::endl;
}

CodePudding user response:

It's very unclear to me what the actual problem you're trying to solve is, but there's a few possibly better ways to approach this.

With if constexpr

template <typename T>
void data_type(T const&) {
    if constexpr (std::is_integral_v<T>) {
        std::cout << "integral\n";
    } else {
        std::cout << "unknown\n";
    }
}

If (as I suspect) your goal is to not bind a reference to integral types (for whatever reason) you can get fancier with C 20 concepts

template <std::integral T>
void data_type(T) {
    std::cout << "integral\n";
}

template <typename T> requires (!std::integral<T>)
void data_type(T const&) {
    std::cout << "unknown\n";
}
  • Related