Difference between const auto & and auto & if object of reference is const
// case 1
const int i = 42;
const auto &k = i;
// case 2
const int i = 42;
auto &k = i;
Do we need the const keyword before auto in this scenario? After all, a reference (k) to an auto-deduced type will include the top level const of the object (const int i). So I believe k will be a reference to an integer that is constant (const int &k) in both cases.
If that is true, does that mean that const auto &k = i; in case 1 is replaced by the compiler as just const int &k = i; (auto being replaced with int)? Whereas in case 2, auto is replaced with const int?
auto keyword automatically decides the type of the variable at compile time.
In your first case, auto is reduced to int where it's reduced to const int in the second case. So, both of your cases are reduced to the same code as:
const int &k = i;
However, it's better to have the const explicitly for better readability and to make sure your variable TRULY is const.
Type deduction with auto works like template argument type deduction with a few exceptions that don't apply in the given example. Hence
const int i = 42;
const auto& k1 = i; // same as const int& k1 = i;
auto& k2 = i; // same as (const int)& k2 = i;
It is probably more readable to add the const qualifier nevertheless.
Here is another example where favoring brevity with auto is misleading:
int *i;
const auto k1 = i; // same as int* const k1 = i;
const auto *k2 = i; // same as const int *k2 = i;
In the first case, the object that i points to can be modified through k1, in the second case, it can't.
Hi and welcome to stack overflow.
As this little test program shows, no matter how you specify the type of k, the compiler will never let you lose the constness of i.
#include <iostream>
#include <type_traits>
#include <string>
#define XSTR(s) STR(s)
#define STR(s) #s
template<class T>
struct type;
template<>
struct type<int>
{
static std::string name() { return "int"; }
};
template<class T>
struct type<T&&>
{
static std::string name() { return type<T>::name() + " &&"; }
};
template<class T>
struct type<T&>
{
static std::string name() { return type<T>::name() + " &"; }
};
template<class T>
struct type<T const>
{
static std::string name() { return type<T>::name() + " const"; }
};
#define REPORT_CONST(decl, var, assign) \
{ \
decl var = assign; \
do_it(STR(decl var = assign;), var); \
}
template<class Var>
void do_it(const char* message, Var&&)
{
std::cout << "case: " << message << " results in type: " << type<Var>::name() << '\n';
}
int main()
{
const int i = 42;
REPORT_CONST(const auto &, k, i);
REPORT_CONST(auto &, k, i);
REPORT_CONST(auto &&, k, std::move(i));
REPORT_CONST(auto const &&, k, std::move(i));
REPORT_CONST(int const&, k, i);
// REPORT_CONST(int &, k, i); // error: binding reference of type 'int&' to 'const int' discards qualifiers
}
Expected results:
case: const auto & k = i; results in type: int const &
case: auto & k = i; results in type: int const &
case: auto && k = std::move(i); results in type: int const &
case: auto const && k = std::move(i); results in type: int const &
case: int const& k = i; results in type: int const &
http://coliru.stacked-crooked.com/a/7c72c8ebcf42c351
Note also the decay of named r-values to l-values.
The accepted answer is correct, i.e. there is no difference in regards to the compiled result. What's important to note is that the auto& version is coupling the const-ness of the k reference with the const-ness of the variable i. I figured since the question is titled 'Difference between const auto & and auto & ...' then it's important to emphasize the pragmatic difference here, in which if you don't put a the const keyword, you cannot guarantee the reference will have this cv-qualification. In some scenarios where this is desired, why leave it to chance that i will remain const in the future?
There is a slight difference in first case auto will be deduced to const int and in the second case to int (as you explcitly stated the const).
cpp-reference states
The keyword auto may be accompanied by modifiers, such as const or &, which will participate in the type deduction. For example, given const auto& i = expr;, the type of i is exactly the type of the argument u in an imaginary template template void f(const U& u) if the function call f(expr) was compiled. Therefore, auto&& may be deduced either as an lvalue reference or rvalue reference according to the initializer, which is used in range-based for loop.
So for you this means
// case 1
const int i = 42;
const auto &k = i; // auto -> int
// case 2
const int i = 42;
auto &k = i; // auto -> const int
More important in my opinion is, however, that the intent is stated more clearly if you state const explcitly and the guarantees the constness. Therefore in this case I would clearly prefer it.
const int i = 42;
const auto &k = i;
// case 2
const int i = 42;
auto &k = i;
Do we need the const keyword before auto in this scenario? After all, a reference (k) to an auto-deduced type will include the top level const of the object (const int i). So I believe k will be a reference to an integer that is constant (const int &k) in both cases.
If that is true, does that mean that const auto &k = i; in case 1 is replaced by the compiler as just const int &k = i; (auto being replaced with int)? Whereas in case 2, auto is replaced with const int?
auto keyword automatically decides the type of the variable at compile time.
In your first case, auto is reduced to int where it's reduced to const int in the second case. So, both of your cases are reduced to the same code as:
const int &k = i;
However, it's better to have the const explicitly for better readability and to make sure your variable TRULY is const.
Type deduction with auto works like template argument type deduction with a few exceptions that don't apply in the given example. Hence
const int i = 42;
const auto& k1 = i; // same as const int& k1 = i;
auto& k2 = i; // same as (const int)& k2 = i;
It is probably more readable to add the const qualifier nevertheless.
Here is another example where favoring brevity with auto is misleading:
int *i;
const auto k1 = i; // same as int* const k1 = i;
const auto *k2 = i; // same as const int *k2 = i;
In the first case, the object that i points to can be modified through k1, in the second case, it can't.
Hi and welcome to stack overflow.
As this little test program shows, no matter how you specify the type of k, the compiler will never let you lose the constness of i.
#include <iostream>
#include <type_traits>
#include <string>
#define XSTR(s) STR(s)
#define STR(s) #s
template<class T>
struct type;
template<>
struct type<int>
{
static std::string name() { return "int"; }
};
template<class T>
struct type<T&&>
{
static std::string name() { return type<T>::name() + " &&"; }
};
template<class T>
struct type<T&>
{
static std::string name() { return type<T>::name() + " &"; }
};
template<class T>
struct type<T const>
{
static std::string name() { return type<T>::name() + " const"; }
};
#define REPORT_CONST(decl, var, assign) \
{ \
decl var = assign; \
do_it(STR(decl var = assign;), var); \
}
template<class Var>
void do_it(const char* message, Var&&)
{
std::cout << "case: " << message << " results in type: " << type<Var>::name() << '\n';
}
int main()
{
const int i = 42;
REPORT_CONST(const auto &, k, i);
REPORT_CONST(auto &, k, i);
REPORT_CONST(auto &&, k, std::move(i));
REPORT_CONST(auto const &&, k, std::move(i));
REPORT_CONST(int const&, k, i);
// REPORT_CONST(int &, k, i); // error: binding reference of type 'int&' to 'const int' discards qualifiers
}
Expected results:
case: const auto & k = i; results in type: int const &
case: auto & k = i; results in type: int const &
case: auto && k = std::move(i); results in type: int const &
case: auto const && k = std::move(i); results in type: int const &
case: int const& k = i; results in type: int const &
http://coliru.stacked-crooked.com/a/7c72c8ebcf42c351
Note also the decay of named r-values to l-values.
The accepted answer is correct, i.e. there is no difference in regards to the compiled result. What's important to note is that the auto& version is coupling the const-ness of the k reference with the const-ness of the variable i. I figured since the question is titled 'Difference between const auto & and auto & ...' then it's important to emphasize the pragmatic difference here, in which if you don't put a the const keyword, you cannot guarantee the reference will have this cv-qualification. In some scenarios where this is desired, why leave it to chance that i will remain const in the future?
There is a slight difference in first case auto will be deduced to const int and in the second case to int (as you explcitly stated the const).
cpp-reference states
The keyword auto may be accompanied by modifiers, such as const or &, which will participate in the type deduction. For example, given const auto& i = expr;, the type of i is exactly the type of the argument u in an imaginary template template void f(const U& u) if the function call f(expr) was compiled. Therefore, auto&& may be deduced either as an lvalue reference or rvalue reference according to the initializer, which is used in range-based for loop.
So for you this means
// case 1
const int i = 42;
const auto &k = i; // auto -> int
// case 2
const int i = 42;
auto &k = i; // auto -> const int
More important in my opinion is, however, that the intent is stated more clearly if you state const explcitly and the guarantees the constness. Therefore in this case I would clearly prefer it.
Comments
Post a Comment