The C++ compiler treats all function arguments symmetrically. Every one is equally important.
template <typename T>
void push(Node<T>** head, T data)
here we have two arguments. When we call push
without explicitly passing it T
, we deduce T
from the arguments.
If you pass it a pointer to a pointer to a Node<float>
and an int
, the compiler gets confused. Is T
a float
, or an int
?
While you could solve it, and we could modify C++ to solve it, the result could easily be surprising. So instead, C++ says "types deduced are not consistent".
We can fix this in a number of ways.
You could pass a float
for data
. push(&head, 0.1f)
or push(&head, float(1))
or whatever.
You can pass the type T
explicitly: push<float>(&head, 1.0)
.
You can block deduction for the 2nd argument. This means that T
gets deduced from the first argument, and never from the 2nd:
template<class T>struct block {using type=T;};
template<class T>using block_deduction = typename block<T>::type;
template <typename T>
void push(Node<T>** head, block_deduction<T> data)
You can realize we don't care what the second arguments type is, and leave it free:
template <typename T, typename U>
void push(Node<T>** head, U data)
You can replace push
with a more modern emplace
that has you pass construction arguments for your T
instead of a T
directly. As copy constructors mean that a variable of type T
is valid, this is a drop-in replacement usually:
template <typename T, typename...Args
void emplace(Node<T>** head, Args&&...args) {
Node<T>* tmp = *head;
Node<T>* newNode = new Node<T>(std::forward<Args>(args)...);
// ...
This is the turbo-charged version. It lets you create Node<non_movable_type>
, and it can lead to efficiencies in other cases, such as types that are cheap to construct but expensive to move around.
All of the above are reasonable solutions, but I find the first two poor, as they leave your interface brittle.