auto
was a storage class specifiervoid f() {
auto int i = 42;
}
auto
can be used in a variable declaration.
auto
alone will always deduce to an object type (not a reference, or const type)const int& a = 42;
auto b = a;
print_type_name<decltype(a)>();
print_type_name<decltype(b)>();
const auto c = b;
const auto& d = b;
print_type_name<decltype(c)>();
print_type_name<decltype(d)>();
decltype(auto)
can be used to declare a variable which matches the expression typedecltype(auto) e = a;
print_type_name<decltype(e)>();
auto&&
is a forwarding referencedecltype
is used with the std::forward
template argumentnamespace {
instrumented some_function() { return instrumented(); }
void some_sink(instrumented) { }
} // namespace
auto&& q = some_function();
some_sink(forward<decltype(q)>(q));
C++11 added trailing return types. This allows a return type which is dependent on an argument type.
namespace {
int function_a();
auto function_b() -> int; // same signature as function_a
template <class T, class U>
auto add(T a, U b) -> decltype(a + b) {
return a + b;
}
} // namespace
template
and inline
functions.inline auto mixed_add(unsigned a, signed b) {
return a + b;
}
auto r = mixed_add(42, -1);
print_type_name<decltype(r)>();
cout << r << "\n";
decltype(auto)
for the return type preserves references and cv-qualifiersnamespace {
vector<int> v = { 0, 1, 2, 3 };
inline decltype(auto) back() {
return v.back();
}
} // namespace
type_name<decltype(back())>();
namespace cpp14 {
template <class T, T N>
struct integral_constant {
static constexpr T value = N;
};
using true_type = integral_constant<bool, true>;
}
template <auto N>
struct integral_constant {
static constexpr decltype(N) value = N;
};
using true_type = integral_constant<true>;
auto [first_name, last_name] = make_pair("Jane"s, "Smith"s);
cout << last_name << ", " << first_name << '\n';
Smith, Jane
int a[] = { 0, 3, 4, 5, 5, 5, 6, 6, 7 };
for(auto [f, l] = equal_range(begin(a), end(a), 5); f != l; ++f) {
cout << *f << '\n';
}
5
5
5
auto
decltype(e)
where is the corresponding memberint a = 1, b = 2;
auto [x, y] = tie(a, b);
x = 42;
cout << a << endl;
42
print_type_name<decltype(x)>();
int&
int a = 1, b = 2;
const auto& [x, y] = tie(a, b);
a = 42;
cout << x << endl;
42
vector<instrumented> w(5);
for (auto e : w) {
// do something
}
for (const auto& e : w) {
// do something
}
for (typename vector<my_class>::const_iterator f = v.begin(), l = v.end;
f != l; ++f) {
for (auto f = v.begin(), l = v.end(); f != l; ++f) {
auto
can make code less brittle to small changes (like size of integral result)
Use for structured binding (better than any alternative)
Otherwise it becomes a matter of taste. Some people recommend using auto
almost never, some people recommend using auto
always. If it makes your code more clear, use it, otherwise do not, but avoid bikeshedding.
bool
and void
auto some_function() -> some_type;
bool is_something();
void got_nothin();
- code aligns better, and it is easier to read
- avoids having trailing result types breaking the style when required
auto some_function() {
//...
return f(g(a + y)); // don't make me figure out this type!
}
- Nearly every time I've omitted the result, I've regretted it