Home > OS >  C : curly brackets with std library type
C : curly brackets with std library type

Time:07-12

I try to understand the docs for std::less, where the following example is given for the usage with a template.

#include <functional>
#include <iostream>
 
template <typename A, typename B, typename C = std::less<>>
bool fun(A a, B b, C cmp = C{})
{
    return cmp(a, b);
}
 
int main()
{
    std::cout
      << std::boolalpha
      << fun(1, 2)   << ' ' // true
      << fun(1.0, 1) << ' ' // false
      << fun(1, 2.0) << ' ' // true
      << std::less<int>{}(5, 5.6)    << ' ' // false: 5 < 5 (warn: implicit conversion)
      << std::less<double>{}(5, 5.6) << ' ' // true: 5.0 < 5.6
      << std::less<int>{}(5.6, 5.7)  << ' ' // false: 5 < 5 (warn: implicit conversion)
      << std::less{}(5, 5.6)         << ' ' // true: less<void>: 5.0 < 5.6
      << '\n';
}

Output:

true false true false true false true

My question is:

How is a std:: function used as a template argument?

And in this concrete case, why are there curly brackets {} behind the C cmp = C or in the call std::less<double>{}(5, 5.6)?

CodePudding user response:

This demo-code suffers from so-called 'uniform initialisation' (broken by design, be careful with its use especially in template code!), classic way to write the same code before uniform initialisation is:

std::less<int>()(5, 5.6); // others analogously

std::less is a callable class, a so-called 'functor', which means it provides an operator() by which objects of the class can be called just like a function (to be precise: the operator is a member function that gets called, just the syntax for getting called actually is the same).

The code presented now first constructs such an object (calls the constructor; first pair of parentheses) while afterwards the so constructed object is called (the operator() with two arguments, second pair of parentheses).

Equivalent code could be:

std::less<int> li; // note that the parentheses need to be left out as you would
                   // declare a function then instead of creating an object;
                   // braces for uniform initialisation could remain
li(5, 5.6);

Note, though, that the life-time of the object li is now longer is longer (until leaving the current scope) than the one of the temporary (only current expression). In given case, there are no visible side effects, though, notably as std::less doesn't have any in its destructor.

CodePudding user response:

std::less is actually not a function, it is a struct, the interface of which may be represented in simplified form as follows.

template<class T>
struct less
{
    bool operator()(const T& t1, const T& t2)
    { return t1 < t2;}
};

In template parameter, as we see, C is actually a type(because std::less<> is a type). So everything makes sense.

CodePudding user response:

How is a std:: function used as a template argument?

It isn't a function. std::less is a class template with one type parameter (which has the default void). It has an operator() member function, which means instances can be used like functions.

why are there curly brackets {}?

To get a default-constructed instance of the class.

std::less<T> has a primary template that compares two Ts via <, but since C 14 std::less<void> is an explicit specialisation. It's operator() is itself a member template with two type parameters, approximately:

template<>
struct less<void>
{
    template<typename T, typename U>
    bool operator()(const T& lhs, const U& rhs)
    { return lhs < rhs;}
};

That is, it can compare things of different types, as long as there is an unambiguous < between them (which may involve implicit conversions).

  • Related