Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What does void mean, or how does it affect T in this case?

So I was doing some work with a friend of mine in C++, and we came across this in the documentation.

//(until C++14)
template<class T>
struct less;
//(since C++14)
template<class T = void>
struct less;

Now I know how it works with class T = int and the same for double and float and other types of classes. But the part I am very confused about, is how does void act in this case? And what are the limitations by using void?

I'm asking this because the C++ documentation is very helpful nor can I find anywhere else that explains it.

like image 324
Nathan van der velde Avatar asked Aug 13 '19 14:08

Nathan van der velde


Video Answer


3 Answers

Given your code snippet I am going to assume you are referring the function object from the standard library, that is, std::less.

In general, the declaration template<class T = void> works exactly as for other types (such as int, for example). In short, when an object of that class is instantiated without specifying the type template argument, then void will be deducted.

std::less<int> li;   // std::less<T = int>;
std::less<void> lv;  // std::less<T = void>;
std::less<> lv2;     // std::less<T = void>; exactly as one row above.

In this particular case, std::less provides a template specialization when T = void.

The object std::less<void> is a handy specialization which allows deducing the types to compare "automatically" with the operator(). Moreover, it needed when you want to compare two different types which are not implicitly convertible.


Practical Example:

Let us assume you have two objects you can compare.

/*Let us assume you have two objects you can compare*/
struct Foo;
struct Bar;

struct Foo {
  bool operator<(const Bar&) const;  
};

struct Bar {
  bool operator<(const Foo&) const;
};

Foo and Bar can be compared to each other, but they are different types.

Which template type will you specify for the functor std::less in this case?

void WrongCode() {
  std::less<Foo> l;
  l(Foo{}, Bar{});  // error
}

If we use std::less<Foo> then the functor will only accept objects of type Foo. (Of course, the same is for std::less<Bar>).

Therefore, the standard provides this handy specialization to cover this case.

void GoodCode() {
  std::less<> l;
  l(Foo{}, Bar{});  // this compile
}

GoodCode will compile because the types of operator() of std::less<void> are automatically deducted (and they can even be different).

like image 117
BiagioF Avatar answered Oct 22 '22 10:10

BiagioF


void is a type.

There are some restrictions on how the void type may be used, but nevertheless, void is a valid type in the type system. Therefore, it is allowed to have void as an argument for a template parameter, including as a default argument.

So, if you type std::less<> then it means std::less<void>.

The question of what std::less<void> means is a separate one: normally, std::less<T> compares T values, but there are no values of type void. Instead, std::less<void> is a special case: you can pass two values of any (possibly different) types to it, as long as they can be compared. See the cppreference.com article on std::less<void>.

like image 25
Brian Bi Avatar answered Oct 22 '22 10:10

Brian Bi


In general it just means that you are going to specialize a class for the type void to process a special case.

Here is a demonstrative program.

#include <iostream>

template <class T = void>
struct A
{
    void operator ()( const T & t ) const 
    { 
        std::cout << "primary template\n"; 
        std::cout << 2 * t << '\n';
    }
};

template <>
struct A<>
{
    template <typename U>
    void operator ()( const U &u ) const 
    { 
        std::cout << "specialization for void\n";
        std::cout << 10 * u << '\n';
    }
};

int main()
{
    A<int>()( 1 );
    A<>()( 1 );
}

Its output is

primary template
2
specialization for void
10
like image 34
Vlad from Moscow Avatar answered Oct 22 '22 09:10

Vlad from Moscow