padamowski
padamowski

Reputation: 159

Variadic template function with more than two parameters

I have the following example where two parameters t1 and t2 are used.

template<typename T>
bool Compare(T t1, T t2)
{
    return t1 == t2;
}

template<typename T, typename... Args> 
bool Compare(T t1, T t2, Args...  args)
{
    return (t1 == t2) && Compare(args...);
}

int main(void)
{
    Compare(1, 1, "string", "string");
}

Function Compare takes pairs of parameters that are the same type and can be compared. Two pairs are compared then parameter pack is passed recursively until the last two parameters are reached. To stop recursion I use an implementation of Compare function without parameter pack.

I would like add the third argument t3 so function Compare should be like this:

template<typename T>
bool Compare(T t1, T t2, T t3)
{
    return t1 == t2 == t3;
}

template<typename T, typename... Args>
bool Compare(T t1, T t2, T t3, Args... args)
{
    return (t1 == t2 == t3) && Compare(args...);
}

int main(void)
{
    Compare(1, 1, 1, "string", "string", "string");
}

I expect that this function takes three parameters to comparison then next three are processed recursively. When I try to compile this code I obtain the following error:

>xxx\source.cpp(4): error C2446: '==': no conversion from 'const char *' to 'int'
1>  xxx\source.cpp(4): note: There is no context in which this conversion is possible
1>  xxx\source.cpp(10): note: see reference to function template instantiation 'bool Compare<const char*>(T,T,T)' being compiled
1>          with
1>          [
1>              T=const char *
1>          ]
1>  xxx\source.cpp(15): note: see reference to function template instantiation 'bool Compare<int,const char*,const char*,const char*>(T,T,T,const char *,const char *,const char *)' being compiled
1>          with
1>          [
1>              T=int
1>          ]
1>xxx\source.cpp(4): error C2040: '==': 'int' differs in levels of indirection from 'const char *'

How to implement this function to compare the sets of three parameters of the same type?

Upvotes: 8

Views: 792

Answers (2)

prestokeys
prestokeys

Reputation: 4849

Here is my more general solution. compareConsecutive<N> will return true only if N consecutive arguments are all equal for all blocks of N arguments.

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

template <std::size_t Start, typename IndexSequence> struct MakeIndexSequenceHelper;

template <std::size_t Start, std::size_t... Is>
struct MakeIndexSequenceHelper<Start, std::index_sequence<Is...>> {
    using type = std::index_sequence<(Start + Is)...>;
};

template <std::size_t Start, std::size_t Length>
struct MakeIndexSequence : MakeIndexSequenceHelper<Start, std::make_index_sequence<Length>> {};

template <typename T, typename U>
bool allAreSame (const T&, const U&) {
    return false;
}

template <typename T>
bool allAreSame (const T& t1, const T& t2) {
    return t1 == t2;
}

template <typename T, typename U, typename... Args>
bool allAreSame (const T&, const U&, const Args&...) {
    return false;
}

template <typename T, typename... Args>
bool allAreSame (const T& t1, const T& t2, const Args&... args) {
    return allAreSame(t1, t2) && allAreSame(t1, args...);
}

template <typename Tuple, std::size_t... Is>
bool allAreSameHelper (Tuple&& tuple, std::index_sequence<Is...>) {
    return allAreSame (std::get<Is>(std::forward<Tuple>(tuple))...);
}

template <std::size_t N, typename... Args>
bool allAreSameHelper (Args&&... args) {
    return allAreSameHelper (std::forward_as_tuple(std::forward<Args>(args)...), std::make_index_sequence<N>{});
}

template <std::size_t N, typename... Args> bool compareConsecutive (Args&&...);

template <std::size_t N>
bool compareConsecutive() {return true;}

template <std::size_t N, typename Tuple, std::size_t... Is>
bool compareConsecutiveHelper (Tuple&& tuple, std::index_sequence<Is...>) {
    return compareConsecutive<N> (std::get<Is>(std::forward<Tuple>(tuple))...);
}

template <std::size_t N, std::size_t Start, std::size_t Length, typename... Args>
bool compareConsecutiveHelper (Args&&... args) {
    return compareConsecutiveHelper<N> (std::forward_as_tuple(std::forward<Args>(args)...), typename MakeIndexSequence<Start, Length>::type{});
}

template <std::size_t N, typename... Args> 
bool compareConsecutive (Args&&... args) {
    return allAreSameHelper<N>(std::forward<Args>(args)...) && compareConsecutiveHelper<N, N, sizeof...(Args) - N>(args...);
}

int main() {
    std::cout << std::boolalpha << allAreSame("hi", "hi", "hi", "hi", "hi", "hi") << '\n';  // true
    std::cout << compareConsecutive<2>(1, 1, "hi", "hi") << '\n';  // true
    std::cout << compareConsecutive<2>(1, "hi", "hi", "hi") << '\n';  // false
    std::cout << compareConsecutive<3>(1, 1, 1, "hi", "hi", "hi", 4.5, 4.5, 4.5) << '\n';  // true
    std::cout << compareConsecutive<5>(1, 1, 1, 1, 1, "hi", "hi", "hi", "hi", "hi") << '\n';  // true
    std::cout << compareConsecutive<5>(1, 1, 1, 1, 2, "hi", "hi", "hi", "hi", "hi") << '\n';  // false
}

Upvotes: 0

TartanLlama
TartanLlama

Reputation: 65600

t1 == t2 == t3

That doesn't check if t1, t2 and t3 are all equal, it checks if t1 equals t2, then checks if the resulting bool is equal to t3.

Maybe what you want instead is (assuming reasonable equality operators):

t1 == t2 && t1 == t3

So your code would look like this:

template<typename T>
bool Compare(T t1, T t2, T t3)
{
    return t1 == t2 && t1 == t3;
}

template<typename T, typename... Args>
bool Compare(T t1, T t2, T t3, Args... args)
{
    return t1 == t2 && t1 == t3 && Compare(args...);
}

Note that your test call with string literals does pointer comparison, which may not be what you want.

Upvotes: 8

Related Questions