You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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.
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 _
}
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:
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)));
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):
In C++26, you can specify a reason why this function is deleted:
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:In C++26, the
unused
variable can be named_
(single underscore):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: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):
On the other hand, the following is not possible:
The following is also not possible, because the declarations are in a namespace scope:
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.
A structure binding can appear in a for-range declaration, such as in the following example:
On the other hand, variables can appear in the condition of an
if
,while
, orfor
statement:However, structure bindings cannot be declared in the condition of an
if
,while
, orfor
statement. That changes in C++26, which makes it possible:An interesting and very useful case is presented in the proposal paper (P0963). Consider the following C++26 example for using
std::to_chars
:When the function succeeds, we are only interested in the
ptr
member ofstd::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 theec
member (of thestd::errc
type) representing an error code.This code can be simplified with structure bindings, in C++26, as follows:
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 constexprstd::format
, although this may also later appear in C++26: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
The text was updated successfully, but these errors were encountered: