Reputation: 807
I formally been told(some books or in web) that the type deduction in auto
is the same as argument type deduction in template
.
However, codes below doesn't compile successfully because it's raises increment of read-only reference a
error. Though the template version deduct T
as type int
instead of const int &
. Why these two cases have different deduction results?
#include "bits/stdc++.h"
using namespace std;
template <typename T>
T minmax2(T n1, T n2) {
n1++;
return n2;
}
int main() {
auto [a, b] = minmax(1, 2); // minmax() return const T &
a++; // compile error: can not alter read-only varaible.
const int & c = 1, & d = 2;
minmax2(c, d); // the same parameter type, don't raise error
}
Another example:
int tmp = 1;
const int & test() {
return tmp;
}
template <typename T>
void test1(T n) {
n++; // no error
}
int main() {
auto a = test(); // If the satement is true, I expect this is the same as test1.
a++; // no compile error, which is different with the previous code exp
const int & b = 2;
test1(b);
}
Upvotes: 0
Views: 109
Reputation: 23537
I think your confusion comes from the fact that structured bindings behave a bit differently than the ordinary auto
, at least from the user's perspective. Consider the latter first:
const int& i = 0;
auto j = i;
std::cout << &i << std::endl;
std::cout << &j << std::endl; // different address
j++; // compiles
Here, i
and j
refer to different objects and the type of j
is int
(not const int
or const int&
). This exactly corresponds with the template argument deduction when the argument is passed by value.
But with structured bindings, the situation is different:
int c = 0, d = 0;
auto [a, b] = std::pair<const int&, const int&>(c, d);
std::cout << &a << std::endl;
std::cout << &c << std::endl; // same address
// a++; // would not compile
Here, the type of a
and b
is const int&
and both a
and c
refer to the same object.
Live demo: https://godbolt.org/z/Wq1zsz
Actually, structured bindings also ignores const and reference parts of an initialization expression, but at an "outer" level, that is, at a level of std::pair
in our case:
const auto& p = std::make_pair(c, d);
auto [x, y] = p;
std::cout << &p.first << std::endl;
std::cout << &x << std::endl; // different address
Here, x
is a new object with type int
. If we used const auto& [x, y] = p;
instead, then x
would refer to p.first
and its type would be const int&
.
Upvotes: 1
Reputation: 16448
The signature of std::minmax
is
template< class T >
std::pair<const T&,const T&> minmax( const T& a, const T& b );
The call
auto [a, b] = minmax(1, 2);
has equivalent behavior to
std::pair<const int&,const int&> temp = minmax(1, 2);
const int &a = temp.first;
const int &b = temp.second;
That is how structured binding works.
As you can see the type T
is deduced as int
. std::minmax
adds a const
and a &
to the type and makes a pair of it.
You could write your own minmax2
as
template <typename T>
std::pair<T,T> minmax2(T n1, T n2) {
return std::make_pair(n1, n2);
}
The call
auto [a, b] = minmax2(1, 2);
has equivalent behavior to
std::pair<int, int> temp = minmax2(1, 2);
int &a = temp.first;
int &b = temp.second;
Here you can
++a;
Upvotes: 0
Reputation: 100
Your comment already states your problem: // minmax() return const T &
After your binding, you essentially have the following variables: const int &a
and const int &b
, afterwards you're trying to increment your const reference of a
which is obviously not working.
To fix your code, you can simply create a copy of a
like this:
auto [a, b] = minmax(1, 2); // minmax() return const T &
auto a_cpy = a;
++a_cpy;
Upvotes: 0