Is it possible for a function to accept only a limited set of types for a given argument?

I know how to do this with any data type with a template:

template<typename T>
T myFunc(T data) { ... }

But is there a way to narrow down the set of valid types, for example, intand charor std::stringand std::wstring, so the compiler will throw an error when it encounters an invalid argument type, and I will get an error at compile time instead of runtime?

edit: Thanks a lot ecatmur, now I understand the whole concept.

template<typename itemA_type, typename itemB_type>
typename std::enable_if<
  (
    std::is_same<itemA_type, int>::value ||
    std::is_same<itemA_type, char>::value) &&
  (
    std::is_same<itemB_type, std::string>::value ||
    std::is_same<itemB_type, std::wstring>::value ||
    std::is_same<itemB_type, const char*>::value ||
    std::is_same<itemB_type, const wchar_t*>::value
  ) ,
  void
>::type
myFunction(itemA_type itemA, itemB_type itemB) {
  using namespace std;
  cout << itemA << itemB << endl;
}
+4
source share
6 answers

A simple solution using enable_ifand is_same:

template<typename T>
typename std::enable_if<
    std::is_same<T, int>::value ||
    std::is_same<T, char>::value,
    T>::type
myFunc(T data) { ... }

T (, string wstring basic_string?), ; .

+5

:

template<typename T, typename U, typename... Us>
struct is_any_of
    : std::integral_constant<
        bool,
        std::conditional<
            std::is_same<T,U>::value,
            std::true_type,
            is_any_of<T,Us...>
        >::type::value
      >
{ };

template<typename T, typename U>
struct is_any_of<T,U> : std::is_same<T,U>::type { };

:

template<typename T>
T myFunc(T data)
{
    static_assert( is_any_of<T, int, char, std::string>{}, "T not allowed");
}

std::is_convertible std::is_constructible std::is_same, .

.

+9

SFINAE:

#include <type_traits>

template<class T> struct AllowedType : std::false_type {};
template<> struct AllowedType<int> : std::true_type {}; // Add more specializations.
template<> struct AllowedType<char> : std::true_type {}; // Add more specializations.

template<typename T>
typename std::enable_if<AllowedType<T>::value, T>::type
myFunc(T data);

int main() {
    myFunc(1);   // int, okay
    myFunc('c'); // char, okay
    myFunc(1.);  // double, fail
}

boost::mpl::vector<> ( , ):

#include <boost/mpl/vector.hpp>
#include <boost/mpl/contains.hpp>
#include <boost/utility/enable_if.hpp>

typedef boost::mpl::vector<int, char> allowed_type_set;

template<typename T>
typename boost::enable_if<boost::mpl::contains<allowed_type_set, T>, T>::type
myFunc(T data);

int main() {
    myFunc(1);   // int, okay
    myFunc('c'); // char, okay
    myFunc(1.);  // double, fail
}
+4

, - , . - :

template <typename...>
struct typelist {
    template <typename U>
    static constexpr bool contains() {
        return false;
    }
};

template <typename Head, typename...Tail>
struct typelist<Head, Tail...> {
    template <typename U>
    static constexpr bool contains() {
        return std::is_same<Head, U>::value || typelist<Tail...>::template contains<U>();
    }
};

myFunc :

template<typename T>
typename std::enable_if<typelist<int,char,std::string>::contains<T>(), T>::type
myFunc(T data);

Coliru.


Concepts Lite, :
template <typename T, typename...Types>
concept bool InList() {
    return typelist<Types...>::template contains<T>();
}

template<InList<int,char,std::string> T>
T myFunc(T data);

.

+3
template<class...>struct types{typedef types type;};
template<class T, class types>struct contains:std::false_type {};
template<class T, class T0, class... Ts>struct contains<T, types<T0, Ts...>>:
  std::integral_constant<bool,
    std::is_same<T, T0>::value || contains<T, types<Ts...>>::value
  >
{};

, SFINAE . std::is_convertible is_same.

typedef types<int,double, std::string> allowed_types;

template<typename T, typename=typename std::enable_if< contains<T, allowed_types>::value >::type>
T myFunc(T data) {
  // code
}

:

namespace details {
  template<typename T>
  T myFunc(T data, std::true_type) {
    // code
  }
}
template<typename T>
T myFunc(T data) {
  return details::myFunc(std::forward<T>(data), contains<T, allowed_types>{});
}

static_assert:

template<typename T>
T myFunc(T data) {
  static_assert(contains<T, allowed_types>{}, "type not allowed");
  // code
}

all of which have various benefits. Although SFINAE seems tempting, I would advise him: it is fragile and can generate crazy error messages.

+2
source

You can use overload, SFINAE, tag manager, or specialization to achieve this. Everyone can produce the desired result.

+1
source

Source: https://habr.com/ru/post/1543086/


All Articles