Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
CPlusPlusNotesForProfessionals.pdf
Скачиваний:
47
Добавлен:
20.05.2023
Размер:
5.11 Mб
Скачать

// the implicit instantiation of X<T**> which ...

};

Section 104.17: Overflow during conversion to or from floating point type

If, during the conversion of:

an integer type to a floating point type,

a floating point type to an integer type, or

a floating point type to a shorter floating point type,

the source value is outside the range of values that can be represented in the destination type, the result is undefined behavior. Example:

double x = 1e100;

int y = x; // int probably cannot hold numbers that large, so this is UB

Section 104.18: Modifying a string literal

Version < C++11

char *str = "hello world"; str[0] = 'H';

"hello world" is a string literal, so modifying it gives undefined behaviour.

The initialisation of str in the above example was formally deprecated (scheduled for removal from a future version of the standard) in C++03. A number of compilers before 2003 might issue a warning about this (e.g. a suspicious conversion). After 2003, compilers typically warn about a deprecated conversion.

Version ≥ C++11

The above example is illegal, and results in a compiler diagnostic, in C++11 and later. A similar example may be constructed to exhibit undefined behaviour by explicitly permitting the type conversion, such as:

char *str = const_cast<char *>("hello world"); str[0] = 'H';

Section 104.19: Accessing an object as the wrong type

In most cases, it is illegal to access an object of one type as though it were a di erent type (disregarding cvqualifiers). Example:

float x = 42;

int y = reinterpret_cast<int&>(x);

The result is undefined behavior.

There are some exceptions to this strict aliasing rule:

An object of class type can be accessed as though it were of a type that is a base class of the actual class type.

Any type can be accessed as a char or unsigned char, but the reverse is not true: a char array cannot be accessed as though it were an arbitrary type.

A signed integer type can be accessed as the corresponding unsigned type and vice versa.

GoalKicker.com – C++ Notes for Professionals

530

A related rule is that if a non-static member function is called on an object that does not actually have the same type as the defining class of the function, or a derived class, then undefined behavior occurs. This is true even if the function does not access the object.

struct Base { };

struct Derived : Base { void f() {}

};

struct Unrelated {}; Unrelated u;

Derived& r1 = reinterpret_cast<Derived&>(u); // ok

r1.f();

// UB

Base b;

 

Derived& r2 = reinterpret_cast<Derived&>(b); //

ok

r2.f();

//

UB

Section 104.20: Invalid derived-to-base conversion for pointers to members

When static_cast is used to convert T D::* to T B::*, the member pointed to must belong to a class that is a base class or derived class of B. Otherwise the behavior is undefined. See Derived to base conversion for pointers to members

Section 104.21: Destroying an object that has already been destroyed

In this example, a destructor is explicitly invoked for an object that will later be automatically destroyed.

struct S {

~S() { std::cout << "destroying S\n"; }

};

int main() { S s; s.~S();

} // UB: s destroyed a second time here

A similar issue occurs when a std::unique_ptr<T> is made to point at a T with automatic or static storage duration.

void f(std::unique_ptr<S> p); int main() {

 

S s;

 

 

 

std::unique_ptr<S> p(&s);

 

 

f(std::move(p)); //

s destroyed upon return from f

}

//

UB: s

destroyed

Another way to destroy an object twice is by having two shared_ptrs both manage the object without sharing ownership with each other.

void f(std::shared_ptr<S> p1, std::shared_ptr<S> p2); int main() {

S* p = new S;

// I want to pass the same object twice...

std::shared_ptr<S> sp1(p); std::shared_ptr<S> sp2(p); f(sp1, sp2);

} // UB: both sp1 and sp2 will destroy s separately

GoalKicker.com – C++ Notes for Professionals

531

//NB: this is correct:

//std::shared_ptr<S> sp(p);

//f(sp, sp);

Section 104.22: Access to nonexistent member through pointer to member

When accessing a non-static member of an object through a pointer to member, if the object does not actually contain the member denoted by the pointer, the behavior is undefined. (Such a pointer to member can be obtained through static_cast.)

struct Base { int

x; };

struct Derived

: Base { int y; };

int Derived::*pdy

= &Derived::y;

int Base::*pby

= static_cast<int Base::*>(pdy);

Base* b1

= new

Derived;

b1->*pby = 42;

// ok; sets y in Derived object to 42

Base* b2

= new

Base;

b2->*pby = 42;

// undefined; there is no y member in Base

 

 

 

 

Section 104.23: Invalid base-to-derived static cast

If static_cast is used to convert a pointer (resp. reference) to base class to a pointer (resp. reference) to derived class, but the operand does not point (resp. refer) to an object of the derived class type, the behavior is undefined. See Base to derived conversion.

Section 104.24: Floating point overflow

If an arithmetic operation that yields a floating point type produces a value that is not in the range of representable values of the result type, the behavior is undefined according to the C++ standard, but may be defined by other standards the machine might conform to, such as IEEE 754.

float x = 1.0;

for (int i = 0; i < 10000; i++) {

x *= 10.0; // will probably overflow eventually; undefined behavior

}

Section 104.25: Calling (Pure) Virtual Members From

Constructor Or Destructor

The Standard (10.4) states:

Member functions can be called from a constructor (or destructor) of an abstract class; the e ect of making a virtual call (10.3) to a pure virtual function directly or indirectly for the object being created (or destroyed) from such a constructor (or destructor) is undefined.

More generally, some C++ authorities, e.g. Scott Meyers, suggest never calling virtual functions (even non-pure ones) from constructors and dstructors.

Consider the following example, modified from the above link:

class transaction

GoalKicker.com – C++ Notes for Professionals

532

{

public:

transaction(){ log_it(); } virtual void log_it() const = 0;

};

class sell_transaction : public transaction

{

public:

virtual void log_it() const { /* Do something */ }

};

Suppose we create a sell_transaction object:

sell_transaction s;

This implicitly calls the constructor of sell_transaction, which first calls the constructor of transaction. When the constructor of transaction is called though, the object is not yet of the type sell_transaction, but rather only of the type transaction.

Consequently, the call in transaction::transaction() to log_it, won't do what might seem to be the intuitive thing - namely call sell_transaction::log_it.

If log_it is pure virtual, as in this example, the behaviour is undefined.

If log_it is non-pure virtual, transaction::log_it will be called.

Section 104.26: Function call through mismatched function pointer type

In order to call a function through a function pointer, the function pointer's type must exactly match the function's type. Otherwise, the behaviour is undefined. Example:

int f();

void (*p)() = reinterpret_cast<void(*)()>(f); p(); // undefined

GoalKicker.com – C++ Notes for Professionals

533