Erich Keane <erich.keane@intel.com>
// Before:
array[x] // Ok
array[(x,y)] // Ok, uses y as index/key
// Ok, uses y as index/key
array[x,y]
// After:
array[x] // Ok
array[(x,y)] // Ok, uses y as index/key
// Deprecated, uses y as index/key
array[x,y]
// Someday we get:
mdspan<int, array_property::dimension<2, 3, 5>> foo(/*...*/);
int value = foo[1, 2, 3];// Already Allowed.
template <typename T>
constexpr T f1(const T& other) {
auto* t = new T; // default init
*t = other;
T out = *t;
delete t;
return out;
}
template <typename T>
constexpr T f2(const T& other) {
T t; // default init
t = other;
T out = t;
return out;
}// Allow f2
template <typename T>
constexpr T f1(const T& other) {
auto* t = new T[1]; // default init
t[0] = other;
T out = t[0];
delete[] t;
return out;
}
template <typename T>
constexpr T f2(const T& other) {
T t[1]; // Previously fails.
t[0] = other;
T out = t[0];
return out;
}// Make this type be trivially copy constructible
// if T is (previously was not).
template <typename T>
struct optional {
optional(optional const&)
requires TriviallyCopyConstructible<T>
&& CopyConstructible<T>
= default;
optional(optional const& rhs)
requires CopyConstructible<T>
: engaged(rhs.engaged)
{
if (engaged) {
new (value) T(rhs.value);
}
}
};P1630: Spaceship needs a tune-up.
Cleanup some interaction between operator==, operator!= and operator<=>
struct data_holder {
private:
std::unique_ptr<char[], data_deleter> ptr;
std::vector<int> indices;
// ...
public:
[[nodiscard("Possible memory leak.")]]
char* release() { indices.clear(); return ptr.release(); }
void clear() { indices.clear(); ptr.reset(nullptr); }
[[nodiscard("Did you mean 'clear'?")]]
bool empty() const { return ptr != nullptr && !indices.empty(); }
};
int main () {
data_holder dh = /* ... */;
// serious error, with explanation
dh.release();
// less serious, with explanation
dh.empty();
return 0;
}// Before
std::string_view
to_string(rgba_color_channel channel) {
switch (channel) {
case rgba_color_channel::red:
return "red";
case rgba_color_channel::green:
return "green";
case rgba_color_channel::blue:
return "blue";
case rgba_color_channel::alpha:
return "alpha";
}
}// After
std::string_view
to_string(rgba_color_channel channel) {
switch (my_channel) {
using enum rgba_color_channel;
case red:
return "red";
case green:
return "green";
case blue:
return "blue";
case alpha:
return "alpha";
}
// Also allowed:
using rgba_color_channel::red;
auto x = red;
}// Given:
template<class T, class U>
struct Unconstrained { Unconstrained(U, V);};
template<class T, class U, class = enable_if_t<is_integral_v<U>>>
struct Sfinae { Sfinae(U, V); };
template<class T, class U> requires Integral<U>
struct Conceptized { Conceptized(U, V); };
// Before:
auto u4 = make_unique<Unconstrained>(3, 5); // OK. unique_ptr<Unconstrained<int, int>>
auto s4 = make_unique<Sfinae>(3, 5); // OK. unique_ptr<Unconstrained<int, int>>
// Ill-formed instead of desired Conceptized<int, int>
auto c4 = make_unique<Conceptized>(3, 5);
// Make this work:
auto c4 = make_unique<Conceptized>(3, 5); // Proposed: Conceptized<int, int>template<class T>
struct Point { T x; T y; };
// Before:
// Aggregate: Cannot deduce
Point<double> p{3.0, 4.0};
Point<double> p2{.x = 3.0, .y = 4.0};
// After:
// Proposed: Aggregates deduce
Point p{3.0, 4.0};
Point p2{.x = 3.0, .y = 4.0}; constexpr double fma(double b, double c, double d) {
if (std::is_constant_evaluated())
return b * c + d; // <ed note>I know, this is wrong.
asm("vfmadd132sd %0 %1 %2"
: "+x"(b)
: "x" (c), "x" (d)
);
return b;
}void f(int(&)[]);
int arr[1];
f(arr); // Error
int(&r)[] = arr; // Error
// Paper allows:
void f(int (&&)[] ); // #1
void f(double (&&)[] ); // #2
void f(int (&&)[2]); // #3
f( {1} ); // Calls #1: Better than #2 due to conversion, better than #3 due to bounds
f( {1.0} ); // Calls #2: Identity conversion is better than floating-integral conversion
f( {1.0, 2.0} );// Calls #2: Identity conversion is better than floating-integral conversion
f( {1, 2} ); // Calls #3: Converting to array of known bound is better than to unknown
// bound, and an identity conversion is better than
// floating-integral conversiontemplate<typename T>
concept R = requires (T i) {
typename T::type;
// No longer allowed.
{*i} -> const typename T::type&;
// Switch to:
{*i} -> ConvertibleTo<const typename T::type&>;
// OR:
{*i} -> Same<const typename T::type&>;
};void foo() {
// Immediately locks then unlocks, no diagnostic.
std::lock_guard<Mutex>(SomeMutex);
...
}
// Change lock_guard ctor to:
template<typename T> class lock_guard {
...
[[nodiscard]]
lock_guard(T &){...}
...
}
void foo() {
// Now warns, you meant to give this a name!
std::lock_guard<Mutex>(SomeMutex);
...
}
Currently, recognizing header unit imports requires full preprocessing which is problematic for dependency scanning and partial preprocessing. This paper proposes changes that will allow handling such imports with the same degree of preprocessing as #include directives.
Import declarations of a header must:
Start with "import" or "export import" tokens, not produced by a macro replacement.
Followed, after macro replacement, by header-name-tokens.
The entire, single, and only declaration is on one line.
Note: Non-header version can still combine lines.
constexpr's allocator traits and the default allocator.
Relaxes the rules to permit non-transient allocations in constexpr time.
Should enable a bunch of STL containers to be used at constexpr time!
string message = format("The answer is {}.", 42);
time_t t = time(nullptr);
string date = format("The date is {0:%Y-%m-%d}.", *localtime(&t));
// ***********centered***********
format("{:*^30}", "centered");