As suggested by Piotr Skotnicki, a simple solution is separate the first argument from the followings and check it with using &&
as fold operator
By example, the following function that return true
if all arguments are equals
template <typename A0, typename ... Args>
bool foo (A0 const & a0, Args const & ... args)
{ return ( (args == a0) && ... && true ); }
Unfortunately this can't work with an empty list of arguments
std::cout << foo(1, 1, 1, 1) << std::endl; // print 1
std::cout << foo(1, 1, 2, 1) << std::endl; // print 0
std::cout << foo() << std::endl; // compilation error
but you can add the special empty argument foo()
bool foo ()
{ return true; }
If, for some reason, you can't split the args
in a a0
and the following args
?
Well... you can obviously use the preceding foo()
function (with special empty version)
template<typename... Args>
void func (Args... args)
{
ASSERT (foo(args));
// more code here...
}
or you can use the C++17 fold expression with comma operator and assignment as in the following bar()
template <typename ... Args>
bool bar (Args const & ... args)
{
auto a0 = ( (0, ..., args) );
return ( (args == a0) && ... && true );
}
Observe the initial zero in a0
assignment that permit the use of this solution also with an empty list of arguments.
Unfortunately, from the preceding auto a0
assignment I get a lot of warnings ("expression result unused", from clang++, and "left operand of comma operator has no effect", from g++) that I don't know how to avoid.
The following is a full working example
#include <iostream>
template <typename A0, typename ... Args>
bool foo (A0 const & a0, Args const & ... args)
{ return ( (args == a0) && ... && true ); }
bool foo ()
{ return true; }
template <typename ... Args>
bool bar (Args const & ... args)
{
auto a0 = ( (0, ..., args) );
return ( (args == a0) && ... && true );
}
int main ()
{
std::cout << foo(1, 1, 1, 1) << std::endl; // print 1
std::cout << foo(1, 1, 2, 1) << std::endl; // print 0
std::cout << foo() << std::endl; // print 1 (compilation error
// witout no argument
// version)
std::cout << bar(1, 1, 1, 1) << std::endl; // print 1
std::cout << bar(1, 1, 2, 1) << std::endl; // print 0
std::cout << bar() << std::endl; // print 1 (no special version)
}
-- EDIT --
As pointed by dfri (thanks!), for and empty args...
pack, the values for the following folded expressions
( (args == a0) && ... )
( (args == a0) || ... )
are, respectively, true
and false
.
So return instruction of foo()
and bar()
can be indifferently written
return ( (args == a0) && ... && true );
or
return ( (args == a0) && ... );
and this is true also in case sizeof...(args) == 0U
.
But I tend to forget this sort of details and prefer to explicit (with the final && true
) the empty-case value.