Getting started with C++TemplatesMetaprogrammingIteratorsReturning several values from a functionstd::stringNamespacesFile I/OClasses/StructuresSmart PointersFunction Overloadingstd::vectorOperator OverloadingLambdasLoopsstd::mapThreadingValue CategoriesPreprocessorSFINAE (Substitution Failure Is Not An Error)The Rule of Three, Five, And ZeroRAII: Resource Acquisition Is InitializationExceptionsImplementation-defined behaviorSpecial Member FunctionsRandom number generationReferencesSortingRegular expressionsPolymorphismPerfect ForwardingVirtual Member FunctionsUndefined BehaviorValue and Reference SemanticsOverload resolutionMove SemanticsPointers to membersPimpl Idiomstd::function: To wrap any element that is callableconst keywordautostd::optionalCopy ElisionBit OperatorsFold ExpressionsUnionsUnnamed typesmutable keywordBit fieldsstd::arraySingleton Design PatternThe ISO C++ StandardUser-Defined LiteralsEnumerationType ErasureMemory managementBit ManipulationArraysPointersExplicit type conversionsRTTI: Run-Time Type InformationStandard Library AlgorithmsFriend keywordExpression templatesScopesAtomic Typesstatic_assertoperator precedenceconstexprDate and time using <chrono> headerTrailing return typeFunction Template OverloadingCommon compile/linker errors (GCC)Design pattern implementation in C++Optimization in C++Compiling and BuildingType Traitsstd::pairKeywordsOne Definition Rule (ODR)Unspecified behaviorFloating Point ArithmeticArgument Dependent Name Lookupstd::variantAttributesInternationalization in C++ProfilingReturn Type CovarianceNon-Static Member FunctionsRecursion in C++Callable Objectsstd::iomanipConstant class member functionsSide by Side Comparisons of classic C++ examples solved via C++ vs C++11 vs C++14 vs C++17The This PointerInline functionsCopying vs AssignmentClient server examplesHeader FilesConst Correctnessstd::atomicsData Structures in C++Refactoring TechniquesC++ StreamsParameter packsLiteralsFlow ControlType KeywordsBasic Type KeywordsVariable Declaration KeywordsIterationtype deductionstd::anyC++11 Memory ModelBuild SystemsConcurrency With OpenMPType Inferencestd::integer_sequenceResource Managementstd::set and std::multisetStorage class specifiersAlignmentInline variablesLinkage specificationsCuriously Recurring Template Pattern (CRTP)Using declarationTypedef and type aliasesLayout of object typesC incompatibilitiesstd::forward_listOptimizationSemaphoreThread synchronization structuresC++ Debugging and Debug-prevention Tools & TechniquesFutures and PromisesMore undefined behaviors in C++MutexesUnit Testing in C++Recursive MutexdecltypeUsing std::unordered_mapDigit separatorsC++ function "call by value" vs. "call by reference"Basic input/output in c++Stream manipulatorsC++ ContainersArithmitic Metaprogramming

auto

Other topics

Remarks:

The keyword auto is a typename that represents an automatically-deduced type.

It was already a reserved keyword in C++98, inherited from C. In old versions of C++, it could be used to explicitly state that a variable has automatic storage duration:

int main()
{
  auto int i = 5; // removing auto has no effect
}

That old meaning is now removed.

Basic auto sample

The keyword auto provides the auto-deduction of type of a variable.

It is especially convenient when dealing with long type names:

std::map< std::string, std::shared_ptr< Widget > > table;
// C++98
std::map< std::string, std::shared_ptr< Widget > >::iterator i = table.find( "42" );
// C++11/14/17
auto j = table.find( "42" );

with range-based for loops:

vector<int> v = {0, 1, 2, 3, 4, 5};
for(auto n: v)
    std::cout << n << ' ';

with lambdas:

auto f = [](){ std::cout << "lambda\n"; };
f();        

to avoid the repetition of the type:

auto w = std::make_shared< Widget >();

to avoid surprising and unnecessary copies:

auto myMap = std::map<int,float>();
myMap.emplace(1,3.14);

std::pair<int,float> const& firstPair2 = *myMap.begin();  // copy!
auto const& firstPair = *myMap.begin();  // no copy!

The reason for the copy is that the returned type is actually std::pair<const int,float>!

auto and Expression Templates

auto can also cause problems where expression templates come into play:

auto mult(int c) {
    return c * std::valarray<int>{1};
}

auto v = mult(3);
std::cout << v[0]; // some value that could be, but almost certainly is not, 3.

The reason is that operator* on valarray gives you a proxy object that refers to the valarray as a means of lazy evaluation. By using auto, you're creating a dangling reference. Instead of mult had returned a std::valarray<int>, then the code would definitely print 3.

auto, const, and references

The auto keyword by itself represents a value type, similar to int or char. It can be modified with the const keyword and the & symbol to represent a const type or a reference type, respectively. These modifiers can be combined.

In this example, s is a value type (its type will be inferred as std::string), so each iteration of the for loop copies a string from the vector into s.

std::vector<std::string> strings = { "stuff", "things", "misc" };
for(auto s : strings) {
    std::cout << s << std::endl;
}

If the body of the loop modifies s (such as by calling s.append(" and stuff")), only this copy will be modified, not the original member of strings.

On the other hand, if s is declared with auto& it will be a reference type (inferred to be std::string&), so on each iteration of the loop it will be assigned a reference to a string in the vector:

for(auto& s : strings) {
    std::cout << s << std::endl;
}

In the body of this loop, modifications to s will directly affect the element of strings that it references.

Finally, if s is declared const auto&, it will be a const reference type, meaning that on each iteration of the loop it will be assigned a const reference to a string in the vector:

for(const auto& s : strings) {
    std::cout << s << std::endl;
}

Within the body of this loop, s cannot be modified (i.e. no non-const methods can be called on it).

When using auto with range-based for loops, it is generally good practice to use const auto& if the loop body will not modify the structure being looped over, since this avoids unnecessary copies.

Trailing return type

auto is used in the syntax for trailing return type:

auto main() -> int {}

which is equivalent to

int main() {}

Mostly useful combined with decltype to use parameters instead of std::declval<T>:

template <typename T1, typename T2>
auto Add(const T1& lhs, const T2& rhs) -> decltype(lhs + rhs) { return lhs + rhs; }

Generic lambda (C++14)

C++14

C++14 allows to use auto in lambda argument

auto print = [](const auto& arg) { std::cout << arg << std::endl; };

print(42);
print("hello world");

That lambda is mostly equivalent to

struct lambda {
    template <typename T>
    auto operator ()(const T& arg) const {
        std::cout << arg << std::endl;
    }
};

and then

lambda print;

print(42);
print("hello world");

auto and proxy objects

Sometimes auto may behave not quite as was expected by a programmer. It type deduces the expression, even when type deduction is not the right thing to do.

As an example, when proxy objects are used in the code:

std::vector<bool> flags{true, true, false};
auto flag = flags[0];
flags.push_back(true);

Here flag would be not bool, but std::vector<bool>::reference, since for bool specialization of template vector the operator [] returns a proxy object with conversion operator operator bool defined.

When flags.push_back(true) modifies the container, this pseudo-reference could end up dangling, referring to an element that no longer exists.

It also makes the next situation possible:

void foo(bool b);

std::vector<bool> getFlags();

auto flag = getFlags()[5];
foo(flag);

The vector is discarded immediately, so flag is a pseudo-reference to an element that has been discarded. The call to foo invokes undefined behavior.

In cases like this you can declare a variable with auto and initialize it by casting to the type you want to be deduced:

auto flag = static_cast<bool>(getFlags()[5]);

but at that point, simply replacing auto with bool makes more sense.

Another case where proxy objects can cause problems is expression templates. In that case, the templates are sometimes not designed to last beyond the current full-expression for efficiency sake, and using the proxy object on the next causes undefined behavior.

Contributors

Topic Id: 2421

Example Ids: 8009,14495,17316,17330,17484,17815

This site is not affiliated with any of the contributors.