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

Class members as friends

class B;

class A { public:

int Func1( B& b );

private:

int Func2( B& b );

};

class B { private: int _b;

//A::Func1 is a friend function to class B

//so A::Func1 has access to all members of B friend int A::Func1( B& );

};

int

A::Func1(

B&

b

)

{

return

b._b;

}

//

OK

int

A::Func2(

B&

b

)

{

return

b._b;

}

//

C2248

Section 23.3: typename

1.When followed by a qualified name, typename specifies that it is the name of a type. This is often required in templates, in particular, when the nested name specifier is a dependent type other than the current instantiation. In this example, std::decay<T> depends on the template parameter T, so in order to name the nested type type, we need to prefix the entire qualified name with typename. For more deatils, see Where and why do I have to put the "template" and "typename" keywords?

template <class T>

auto decay_copy(T&& r) -> typename std::decay<T>::type;

2. Introduces a type parameter in the declaration of a template. In this context, it is interchangeable with class.

template <typename T>

const T& min(const T& x, const T& y) { return b < a ? b : a;

}

Version ≥ C++17

3.typename can also be used when declaring a template template parameter, preceding the name of the parameter, just like class.

template <template <class T> typename U> void f() {

U<int>::do_it(); U<double>::do_it();

}

GoalKicker.com – C++ Notes for Professionals

118

Section 23.4: explicit

1.When applied to a single-argument constructor, prevents that constructor from being used to perform implicit conversions.

class MyVector { public:

explicit MyVector(uint64_t size);

};

MyVector v1(100); // ok uint64_t len1 = 100;

MyVector v2{len1}; // ok, len1 is uint64_t int len2 = 100;

MyVector v3{len2}; // ill-formed, implicit conversion from int to uint64_t

Since C++11 introduced initializer lists, in C++11 and later, explicit can be applied to a constructor with any number of arguments, with the same meaning as in the single-argument case.

struct S {

explicit S(int x, int y);

};

S f() {

return {12, 34}; // ill-formed return S{12, 34}; // ok

}

Version ≥ C++11

2.When applied to a conversion function, prevents that conversion function from being used to perform implicit conversions.

class C {

const int x; public:

C(int x) : x(x) {}

explicit operator int() { return x; }

};

 

C c(42);

 

int x = c;

// ill-formed

int y = static_cast<int>(c); // ok; explicit conversion

Section 23.5: sizeof

A unary operator that yields the size in bytes of its operand, which may be either an expression or a type. If the operand is an expression, it is not evaluated. The size is a constant expression of type std::size_t.

If the operand is a type, it must be parenthesized.

It is illegal to apply sizeof to a function type.

It is illegal to apply sizeof to an incomplete type, including void.

If sizeof is applied to a reference type T& or T&&, it is equivalent to sizeof(T).

When sizeof is applied to a class type, it yields the number of bytes in a complete object of that type, including any padding bytes in the middle or at the end. Therefore, a sizeof expression can never have a value of 0. See layout of object types for more details.

GoalKicker.com – C++ Notes for Professionals

119

The char, signed char, and unsigned char types have a size of 1. Conversely, a byte is defined to be the amount of memory required to store a char object. It does not necessarily mean 8 bits, as some systems have char objects longer than 8 bits.

If expr is an expression, sizeof(expr) is equivalent to sizeof(T) where T is the type of expr.

int a[100];

std::cout << "The number of bytes in `a` is: " << sizeof a; memset(a, 0, sizeof a); // zeroes out the array

Version ≥ C++11

The sizeof... operator yields the number of elements in a parameter pack.

template <class... T> void f(T&&...) {

std::cout << "f was called with " << sizeof...(T) << " arguments\n";

}

Section 23.6: noexcept

Version ≥ C++11

1.A unary operator that determines whether the evaluation of its operand can propagate an exception. Note that the bodies of called functions are not examined, so noexcept can yield false negatives. The operand is not evaluated.

#include <iostream> #include <stdexcept>

void foo() { throw std::runtime_error("oops"); } void bar() {}

struct S {}; int main() {

std::cout << noexcept(foo()) << '\n'; // prints 0 std::cout << noexcept(bar()) << '\n'; // prints 0 std::cout << noexcept(1 + 1) << '\n'; // prints 1 std::cout << noexcept(S()) << '\n'; // prints 1

}

In this example, even though bar() can never throw an exception, noexcept(bar()) is still false because the fact that bar() cannot propagate an exception has not been explicitly specified.

2.When declaring a function, specifies whether or not the function can propagate an exception. Alone, it declares that the function cannot propagate an exception. With a parenthesized argument, it declares that the function can or cannot propagate an exception depending on the truth value of the argument.

void f1()

{ throw std::runtime_error("oops"); }

void f2()

noexcept(false) { throw std::runtime_error("oops"); }

void f3()

{}

void f4()

noexcept {}

void f5()

noexcept(true) {}

void f6()

noexcept {

try {

 

f1();

} catch (const std::runtime_error&) {}

}

GoalKicker.com – C++ Notes for Professionals

120

In this example, we have declared that f4, f5, and f6 cannot propagate exceptions. (Although an exception can be thrown during execution of f6, it is caught and not allowed to propagate out of the function.) We have declared that f2 may propagate an exception. When the noexcept specifier is omitted, it is equivalent to noexcept(false), so we have implicitly declared that f1 and f3 may propagate exceptions, even though exceptions cannot actually be thrown during the execution of f3.

Version ≥ C++17

Whether or not a function is noexcept is part of the function's type: that is, in the example above, f1, f2, and f3 have di erent types from f4, f5, and f6. Therefore, noexcept is also significant in function pointers, template arguments, and so on.

void g1() {}

void g2() noexcept {}

void (*p1)() noexcept = &g1; // ill-formed, since g1 is not noexcept void (*p2)() noexcept = &g2; // ok; types match

void

(*p3)()

=

&g1;

//

ok;

types match

void

(*p4)()

=

&g2;

//

ok;

implicit conversion

GoalKicker.com – C++ Notes for Professionals

121