Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Whats new in C26 (part 1) #11788

Open
guevara opened this issue Sep 11, 2024 · 0 comments
Open

Whats new in C26 (part 1) #11788

guevara opened this issue Sep 11, 2024 · 0 comments

Comments

@guevara
Copy link
Owner

guevara commented Sep 11, 2024

What’s new in C++26 (part 1)



https://ift.tt/27kZilC






The C++26 version of the C++ standard is a work in progress, but a series of language and library features have been already added. Furthermore, some of them are already supported by Clang and GCC. One of these new changes was discussed in my previous article, Erroneous behaviour has entered the chat. In this post, we will look at several language features added in C++26.

Specifying a reason for deleting a function

Since C++11, we can declare a function as deleted, so that the compiler will prevent its use. This can be used to prevent the use of class special member functions, but also to delete any other function. A function can be deleted as follows (example from the proposal paper):

class NonCopyable
{
public:
    // ...
    NonCopyable() = default;
// copy members
NonCopyable(const NonCopyable&) = delete;
NonCopyable& operator=(const NonCopyable&) = delete;
// maybe provide move members instead

};

In C++26, you can specify a reason why this function is deleted:

class NonCopyable
{
public:
    // ...
    NonCopyable() = default;
// copy members
NonCopyable(const NonCopyable&)
    = delete("Since this class manages unique resources, copy is not supported; use move instead.");
NonCopyable& operator=(const NonCopyable&)
    = delete("Since this class manages unique resources, copy is not supported; use move instead.");
// provide move members instead

};

The reason for having this feature is to help API authors to provide tailored messages for the removal of a function, instead of just relying on the generic compiler error for using a deleted function.

For more info see: P2573R2: = delete(“should have a reason”);

Placeholder variables with no name

There are cases when a variable has to be declared but its name is never used. An example is structure bindings. Another is locks (like lock_guard), that are only used for their side-effects. In the future, another example could be pattern matching (for which several proposals exist).

In C++26, we can use a single underscore (_) to define an unnamed variable.

For instance, in the following example, unused is a variable that is not used:

[[maybe_unused]] auto [data, unused] = get_data();

In C++26, the unused variable can be named _ (single underscore):

auto [data, _] = get_data();

When the single underscore identifier is used for the declaration of a variable, non-static class member variable, lambda capture, or structure binding, the [[maybe_unused]] attribute is implicitly added, therefore, there is no need to explicitly use it.

A declaration with the name _ is said to be name-independent if it declares:

  • a variable with automatic storage duration
  • a structure binding, but not in a namespace scope
  • a variable introduced by an init capture
  • a non-static data member

The compiler will not emit warnings that a name-independent declaration is used or not. Moreover, multiple name-independent declarations can be used in the same scope (that is not a namespace scope):

int main()
{
  int _;
  _ = 0;         // OK

std::string ; // OK, because _ is a name-independent declaration
_ = "0"; // Error: ambiguous reference to placeholder '
', which is defined multiple times
}

On the other hand, the following is not possible:

int main()
{
  int _;
  _ = 0;                // OK

static std::string _; // Error: static variables are not name-independent
}

The following is also not possible, because the declarations are in a namespace scope:

namespace n
{
  int f() {return 42;}

auto _ = f(); // OK
auto _ = f(); // Error: redefinition of _
}

To learn more about this feature see: P2169: A nice placeholder with no name.

Structured binding declaration as a condition

A structure binding defines a set of variables that are bound to sub-objects or elements of their initializer.

auto [position, length] = get_next_token(text, offset);

A structure binding can appear in a for-range declaration, such as in the following example:

for (auto [position, length] : tokenize(text, offset))
{
  std::println("pos {}, len {}", position, length);
}

On the other hand, variables can appear in the condition of an if, while, or for statement:

if (auto it = std::find_if(begin(arr), end(arr), is_even); it != std::end(arr))
{
  std::println("{} is the 1st even number", *it);
}

However, structure bindings cannot be declared in the condition of an if, while, or for statement. That changes in C++26, which makes it possible:

if(auto [position, length] = get_next_token(text, offset); position >= 0)
{
  std::println("pos {}, len {}", position, length);
}

An interesting and very useful case is presented in the proposal paper (P0963). Consider the following C++26 example for using std::to_chars:

if (auto result = std::to_chars(p, last, 42))
{
​​​​    auto [ptr, _] = result;
​​​​    // okay to proceed
​​​​} 
else 
{
​​​​    auto [ptr, ec] = result;
​​​​    // handle errors
​​​​}

When the function succeeds, we are only interested in the ptr member of std::to_chars_result, which contains a pointer to the one-past-the-end pointer of the characters written. If the function fails, then we also need to look at the ec member (of the std::errc type) representing an error code.

This code can be simplified with structure bindings, in C++26, as follows:

​​​​if (auto [ptr, ec] = std::to_chars(p, last, 42))
{
​​​​    // okay to proceed
​​​​} 
else 
{
​​​​    // handle errors
​​​​}

To learn more about this feature see: P0963: Structured binding declaration as a condition.

user-generated static_assert messages

The static_assert‘s second parameter, which is a string representing the error message, can now be a compile-time user-generated string-like object. The following example uses a hypothetical constexpr std::format, although this may also later appear in C++26:

static_assert(sizeof(int) == 4, std::format("Expected 4, actual {}", sizeof(int)));

To learn more about this feature see: P2471R3: user-generated static_assert messages.







via Marius Bancila's Blog | About code. Mostly on C++

September 11, 2024 at 05:30PM
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant