2

Is there a way to compare the values of each type of a variadic template?

template<typename... S>
class Signature {
    // this is not valid syntax
    S... values;

    bool equals(S... s) {
        // this is not either
        bool eq = true;
        eq &= s... &= values...;
        return eq;
    }
};

Example:

Signature<int, bool> s(5, true);
s.equals(5, true); // should result in 1
2
  • c++11, c++14 or c++17? Commented Sep 24, 2017 at 15:12
  • Does not matter (, but c++11 would be nice). Commented Sep 24, 2017 at 15:13

3 Answers 3

4

This:

S... values;

is ill-formed. You cannot have a pack declaration like that (unfortunately). You have to put all the values in something, like:

std::tuple<S...> values;

And once you do that, comparing them is simple. Just use ==:

template<typename... S>
struct Signature {
    std::tuple<S...> values;

    bool equals(Signature<S...> const& rhs) const {
        return values == rhs.values;
    }

    bool equals(S const&... rhs) const {
        // forward here to produce a tuple<S const&...> and avoid an unnecessary copy
        return values == std::forward_as_tuple(rhs...);
    }
};
Sign up to request clarification or add additional context in comments.

2 Comments

Wow, that is simple! Why do you name your variables rhs? I am asking just for interest.
@PaulProgrammerNoob Just cause it's on the right-hand-side of equals.
2

In we simply do:

std::tuple<S...> values;
bool equals(S const&...s) const {
  return std::apply([&](S const&... values){
    return ((values==s)&&...);
  }, values);
}

as S... values; is not legal.

In , the best way is to write your own apply:

namespace details{
  template<std::size_t...Is, class F, class Tuple>
  decltype(auto) apply(std::index_sequence<Is...>, F&& f, Tuple&&t){
    using std::get;
    return std::forward<F>(f)( get<Is>( std::forward<Tuple>(t) )... );
  }
}
template<class F, class Tuple>
decltype(auto) apply( F&& f, Tuple&& tuple )
  using dT=std::decay_t<Tuple>;
  auto size=std::tuple_size<dT>{};
  return details::apply( std::make_index_sequence<size>{}, std::forward<F>(f), std::forward<Tuple>(tuple) );
}

and put it in namespace notstd. You also need to rrplace the fold:

return ((values==s)&&...);

with

bool r = true;
using discard=int[];
(void)discard{0,(void(
  r = r && (values==s)
),0)...};
return r;

In you'll need to replace the decltype(auto) with trailing return types and implement your own index sequence code.

Comments

1

The real problem I can see is ... what is

S ... values;

in your class?

That isn't C++, as far I know.

I suppose you can save your values in a std::tuple

std::tuple<S...> value;

so (if you don't want put your s in a std::tuple and compare the tuples, that is simple but isn't funny) the real problem in your code is connected with the use of the tuple.

I propose the following solution in C++17, based on the new fold expression

#include <tuple>
#include <utility>
#include <iostream>

template <typename ... Ts>
struct Signature
 {    
   std::tuple<Ts...> values;

   Signature (Ts && ... ts) : values { std::forward<Ts>(ts) ... }
    { }

   template <std::size_t ... Is>
   bool equalsH (std::index_sequence<Is...> const &, Ts const & ... ts)
    { return ((ts == std::get<Is>(values)) && ... ); }

   bool equals (Ts const & ... ts)
    { return equalsH(std::make_index_sequence<sizeof...(Ts)>{}, ts...); }
 };

int main ()
 {
   Signature<int, bool> s { 5, true };

   std::cout << s.equals(5, true) << std::endl;  // print 1
   std::cout << s.equals(5, false) << std::endl; // print 0
   std::cout << s.equals(6, false) << std::endl; // print 0
   std::cout << s.equals(6, true) << std::endl;  // print 0
 }

In C++14 you can't use fold expression like in C++17 so you have to modify the helper function (equalH()); I propose the following

   template <std::size_t ... Is>
   bool equalsH (std::index_sequence<Is...> const &, Ts const & ... ts)
    { 
      using unused = int[];

      bool ret { true };

      (void)unused { 0, (void(ret &= (ts == std::get<Is>(values))), 0) ... };

      return ret;
    }

Unfortunately std::make_index_sequence and std::index_sequence are available only starting from C++14, so the preceding example doesn't work in C++11; but create a substitute of std::make_index_sequence and std::index_sequence isn't difficult and, if you want it, you can use the tuple-compare solution

bool equals (Ts && ... ts)
 { return values == std::forward_as_tuple( std::forward<Ts>(ts) ... ); }

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.