james
james

Reputation: 1155

check if the n-th variadic template argument is of a certain type

I'd like to implement something like same_type() function in C++11 like below (this is what I have tried so far but it can not cope with the use cases mentioned below). The function is to check if T is of the same type of the n-th argument of Args, currently n=0 should be enough for my requirement, although n=other meaningful value would be something better to have (not very important if not straightforward).

template<typename T, typename... Args>
struct MyClass
{
    // check if T is of the same type of the n-th argument of Args
    bool same_type() {
        // currently, I only check the first argument
        // but if possible, it would be more useful to extend 
        // this function to check the n-th argument
        return std::is_same<T,typename std::tuple_element<0, std::tuple<Args...> >::type>;
    }
};

I have already had a look at this answer, but it does not consider the following use cases.

Use cases:

1.use with references and const qualifier:

MyClass<int,const int&> c;
// expect to see 1, type of int should match const int&, i.e. reference or const should NOT affect the result
std::cout<<c.same_type();    

2.use with no argument supplied:

MyClass<int> c;
// expect to see 0 as there is no variadic argument provided to compare with int
std::cout<<c.same_type();    

Upvotes: 0

Views: 359

Answers (2)

Richard Hodges
Richard Hodges

Reputation: 69854

I think you're looking to check compatibility more than 'sameness'. here's one way:

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


template<class...Ts>
struct nth_is_compatible
{
  using tuple = std::tuple<Ts...>;

  template<class T, std::size_t N> static constexpr bool check()
  {
    return std::is_convertible<decltype(std::get<N>(std::declval<tuple>())), T>::value;
  }

};

struct Monkey
{
  Monkey(std::string) {} // conversion constructor
};

int main()
{

  using checklist = nth_is_compatible<const int&, float, std::string>;
  constexpr auto list = checklist();

  std::cout << list.check<int, 0>() << std::endl;
  std::cout << list.check<int, 1>() << std::endl;
  std::cout << list.check<int, 2>() << std::endl;

  // prove it's a constexpr and that it works for conversions
  constexpr auto monkeyable = checklist::check<Monkey, 2>();
  std::cout << monkeyable << std::endl;
}

Upvotes: 1

max66
max66

Reputation: 66200

I propose to develop a type traits isSameNth as follows

template <std::size_t, typename...>
struct isSameNth;

template <std::size_t N, typename T, typename A0, typename ... As>
struct isSameNth<N, T, A0, As...> : public isSameNth<N-1U, T, As...>
 { };

template <std::size_t N, typename T>
struct isSameNth<N, T> : public std::false_type
 { };

template <typename T, typename A0, typename ... As>
struct isSameNth<0U, T, A0, As...> : public std::is_same<
   typename std::remove_reference<T>::type const,
   typename std::remove_reference<A0>::type const>
 { };

to transform same_type() in a template static method (where the template value is N)

template <typename T, typename... Args>
struct MyClass
 {
   template <std::size_t N>
   static constexpr bool same_type()
    { return isSameNth<N, T, Args...>::value; }
 };

The following is a full example (C++11 compliant)

#include <type_traits>

template <std::size_t, typename...>
struct isSameNth;

template <std::size_t N, typename T, typename A0, typename ... As>
struct isSameNth<N, T, A0, As...> : public isSameNth<N-1U, T, As...>
 { };

template <std::size_t N, typename T>
struct isSameNth<N, T> : public std::false_type
 { };

template <typename T, typename A0, typename ... As>
struct isSameNth<0U, T, A0, As...> : public std::is_same<
   typename std::remove_reference<T>::type const,
   typename std::remove_reference<A0>::type const>
 { };

template <typename T, typename... Args>
struct MyClass
 {
   template <std::size_t N>
   static constexpr bool same_type()
    { return isSameNth<N, T, Args...>::value; }
 };

int main ()
 {
   static_assert(
      false == MyClass<int, long, int, short>::template same_type<0U>(), "!");
   static_assert(
      true == MyClass<int, long, int, short>::template same_type<1U>(), "!");
   static_assert(
      false == MyClass<int, long, int, short>::template same_type<2U>(), "!");

   static_assert(
      true == MyClass<int const, int &>::template same_type<0U>(), "!");

   static_assert(
      false == MyClass<int const &>::template same_type<0U>(), "!");
 }

Upvotes: 1

Related Questions