0

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();    
james
  • 1,107
  • 14
  • 29
  • What do you mean by "same". It looks is if you mean "in some way compatible". It would help if you could specify that relationship. – Richard Hodges Nov 16 '17 at 22:08

2 Answers2

1

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>(), "!");
 }
max66
  • 65,235
  • 10
  • 71
  • 111
  • Many thanks for your answer. Is it possible to stick to my interface so that I can use something like this? MyClass c; bool flag = c.same_type(); I didn't know it could be so complicated to implement this. – james Nov 16 '17 at 21:22
  • @james - if you remove the `static` in definition of `same_type()` (but I suggest to add a `const` at the end, if `same_type()` doesn't modify the object), you can use the class as `MyClass c; bool flag = c.template same_type<0U>()`; if you remove also the `template ` part, you can write `bool flag = c.same_type()` (but, in this way, you have to hard code the `N`, I suppose). – max66 Nov 16 '17 at 23:38
1

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;
}
Richard Hodges
  • 68,278
  • 7
  • 90
  • 142
  • Thanks for your answer. I should have clarified that I would like to compare two types with cv-qualifiers and reference removed, so int!=float. – james Nov 17 '17 at 09:12
  • I'd like to add that this implementation does not seem to cover my use case 2, i.e. when there is no variadic argument supplied? – james Nov 17 '17 at 09:16