I'm looking at some code pertaining to the n3960 standard proposal and noticed some functions have parameters with no name, yet have a full function definition. Could someone explain how this is?
Example:
template <typename ExPolicy, typename IteratorTag>
void test_for_each(ExPolicy const& policy, IteratorTag) //just IteratorTag, no name?
{
BOOST_STATIC_ASSERT(hpx::parallel::is_execution_policy<ExPolicy>::value);
typedef std::vector<std::size_t>::iterator base_iterator;
typedef test::test_iterator<base_iterator, IteratorTag> iterator;
std::vector<std::size_t> c(10000);
std::iota(boost::begin(c), boost::end(c), std::rand());
hpx::parallel::for_each(policy,
iterator(boost::begin(c)), iterator(boost::end(c)),
[](std::size_t& v) {
v = 42;
});
// verify values
std::size_t count = 0;
std::for_each(boost::begin(c), boost::end(c),
[](std::size_t v) {
HPX_TEST_EQ(v, std::size_t(42));
++count;
});
HPX_TEST_EQ(count, c.size());
}
As noted, argument names are optional. But why was this omitted, and why have an argument without a name?
The technique being used here is function template tag type deduction based off of argument types.
You can call
test_for_eachpassing in an instance of an arbitrary type as the 2nd parameter. Whatever the value type for that argument ends up being passed to thetemplatefunction asIteratorTag.Within the class, the value of the
IteratorTagvariable is not used -- all we care about is the type.IteratorTags are used to distinguish between the various kinds ofstdlibrary iterators -- forward, random access, input, output, etc.With that type in hand, we can make subtle changes to how our code behaves (or less subtle, using overloading). In this case, the
iteratortype within the function takes theIteratorTagtype as one of itstemplatearguments, so the type of that variable differs based on theIteratorTagpassed in.Here is a simple version of this using the tag for a technique called "tag dispatching":
here the
smart_floorfunction takes an arbitrary typeT, and if and only if it is a non-integral type callsflooron it. Otherwise, it just converts it to anint.We create an instance of the tag type -- in this case,
std::is_integral< typename std::decay<T>::type >-- and pass it to an internal helper function. In this case, we have two overloads, neither of which uses the value of the passed in tag, just its type.The above implementation is similar, except it has 1 overload for both cases. Maybe that tag type will be used deeper in in a similar overloading way, or maybe it is specialized in some traits class at some deeper level of use.
Odds are the argument here is supposed to be
std::iterator_traits< T >::iterator_categoryor some homebrewboostvariant, as an aside.