Reputation: 2488
Consider the following templated function:
template <template<class, class> class V, class T, class Allocator>
void fn(V<T, Allocator>& v)
{
for (T& t : v) {
std::cout << t << std::endl;
}
}
When I pass it a non-const
vector like:
std::vector<int> v{ 1, 2, 3, 4, 5 };
fn(v);
The compiler is happy, and I am happy. However, when I pass fn()
a const
vector like:
const std::vector<int> v{ 1, 2, 3, 4, 5 };
fn(v);
All hell breaks loose.It will result in a compilation error. With GCC 4.9.2, the error message is:
In function 'int main()':
17:7: error: invalid initialization of reference of type 'std::vector<int>&' from expression of type 'const std::vector<int>'
7:6: note: in passing argument 1 of 'void fn(V<T, Allocator>&) [with V = std::vector; T = int; Allocator = std::allocator<int>]'
I thought that V<T, Allocator>&
will be deduced to const std::vector<int, std::allocator<int>>&
. However, this is not the case. Why is this so?
Upvotes: 2
Views: 87
Reputation: 39151
I thought that
V<T, Allocator>&
will be deduced toconst std::vector<int, std::allocator<int>>&
This would be true if the parameter was plain U
, like template<class U> void fn(U&);
.
Type deduction is pattern matching. const
is part of the type, hence the pattern U
can be deduced to T const
. Consider:
using T = int const;
T var;
template<class U>
void fn(U&);
fn(var); // -> fn<int const>(int const&);
// We deduce U = T = int const
Type aliases are transparent, so we never actually deduce T
. The const
ness however is still part of the type of var
. The compiler then "invents" a new type alias in form of the template parameter, as if:
void fn<int const>(int const&)
{
using U = int const;
// ...
}
However, the pattern V<X>
is a template instantiation, not a any type. In the end, we need to make the function prototype compatible to the call site. So we need to deduce V
such that we get void fn(vector<int> const);
. This would only work if we deduce V
to be a metafunction:
template<class U>
using M = vector<U> const;
template<template<class> class V, class T>
void fn(V<T>&);
M<int> var;
fn(var); // ?? fn<M, int>(M<int>);
As with type aliases, alias templates are transparent - they're never deduced themselves. The second you write M<int>
it gets resolved to vector<int> const
and we forget about the alias template/metafunction being in use.
The compiler could "invent this metafunction" when it sees the pattern V<T>
and the type vector<int> const
, but I think this "inventing of the metafunction" is just not supported. Neither is augmentation of metafunctions like vector const
(without template arguments, to keep it an unapplied metafunction). Metafunctions are not really a thing in the language, we deal with transparent alias templates (which are never deduced) and actual class templates which don't support the notion of adding/removing const
(they don't deal with qualifiers, just classes).
If it were supported, it could behave as if:
void fn<..>(vector<int> const&)
{
template<class T>
using U = vector<T> const;
// ...
}
Upvotes: 3