1

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());
}
hkaiser
  • 11,403
  • 1
  • 30
  • 35
Syntactic Fructose
  • 18,936
  • 23
  • 91
  • 177
  • 2
    I don't understand the question. Parameter names are optional – sometimes one needs a parameter to conform to a specified interface, but they don't need the value in the actual implementation... And sometimes a parameter is there just for overloading purposes, where the type matters but the value does not. What are you asking about specifically? `test_for_each` is clearly not in N3960... – ildjarn May 30 '14 at 17:50
  • 2
    @ildjarn I did not know parameter names were optional in a function definition, that would be why. If you want to make an answer out of that and expand a little I'll accept yours – Syntactic Fructose May 30 '14 at 17:51
  • I'll let someone else give a fully fleshed out answer, but I'm glad this clarified things for you a bit. :-] – ildjarn May 30 '14 at 17:52
  • @ildjarn `test_for_each` is the rough hpx implementation of `n3960` towards parallelizing the STL – Syntactic Fructose May 30 '14 at 17:53
  • 1
    Ah, apologies, I was thinking of N3290, my brain isn't firing on all cylinders yet today. – ildjarn May 30 '14 at 17:54

4 Answers4

6

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_each passing in an instance of an arbitrary type as the 2nd parameter. Whatever the value type for that argument ends up being passed to the template function as IteratorTag.

Within the class, the value of the IteratorTag variable is not used -- all we care about is the type.

IteratorTags are used to distinguish between the various kinds of std library 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 iterator type within the function takes the IteratorTag type as one of its template arguments, so the type of that variable differs based on the IteratorTag passed in.

Here is a simple version of this using the tag for a technique called "tag dispatching":

template<typename T>
int floor_to_int( T&& t, std::true_type ) { return std::forward<T>(t); }
template<typename T>
int floor_to_int( T&& t, std::false_type ) { return floor(std::forward<T>(t)); }
template<typename T>
int smart_floor( T&& t ) {
  return floor_to_int( std::forward<T>(t), std::is_integral< typename std::decay<T>::type >{} );
}

here the smart_floor function takes an arbitrary type T, and if and only if it is a non-integral type calls floor on it. Otherwise, it just converts it to an int.

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_category or some homebrew boost variant, as an aside.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
3

If you do not actually need the value of the parameter, but must provide a certain interface, you can use an unnamed parameter. This is the correct way to silence the (important) unused parameter warning.

Baum mit Augen
  • 49,044
  • 25
  • 144
  • 182
1

In a function prototype, argument names are optional. They might be useful as documentation, but they are not used by the compiler in any meaningful way.

In a function definition, if an argument is not used in the function's body, its name is again optional:

Foo some_function(Bar bar, Baz)
{
    // this is the body of a function that
    // does not use its second argument
}
isekaijin
  • 19,076
  • 18
  • 85
  • 153
0

The argument name is left out in the following use cases, that I can think of:

  1. Virtual member functions. A derived class implementation may or may not need an argument. If it does not need the argument, the developer has the option to leave it out.

  2. To resolve an overload function based on just the type where the value of the argument is not useful.

R Sahu
  • 204,454
  • 14
  • 159
  • 270