C++ Language & Library Wishlist
There are still a few things that I can't do in C++, that I wish I could.
Some of these will turn out to be dumb ideas. Perhaps all. Then again, a lot of my older wishes have already been fulfilled in some way!
If you wish to discuss any of these, please open a discussion on the libpqxx Github project and tag it as "C++ wishlist".
Here are the wishes, roughly from newest to oldest:
Imprecise output buffers
(May 2026)
Some functions write their output into some output buffer that the caller
passes in. Often it's a std::span<char> referring to caller-allocated
storage in which the function will compose some kind of text.
I suspect that some of these functions are less efficient than they could be, just because the compiler has no way of knowing that I don't care how the data goes into that buffer, so long as it's correct after the function exits. (Often the data is also useless if the function exits with an exception.)
For example, the compiler may not be able to vectorise a loop because a signal handler might look at that output buffer before the function is done, and see that it isn't being written in the way or order that it might expect.
Would it help if we could tell the compiler that it doesn't need to be particular about the buffer's intermediate states?
One way to do that might be a special attribute. But it may not generalise
well: if you apply the attribute to a std::span object, what you really want
is for the compiler to propagate that to the underlying pointer/size pair. But
does that always have a clear meaning when the argument is not a span?
Here's another way: you work in a local buffer (e.g. on the stack), and then
at the very end you copy or move that buffer into a place where it becomes
available to the caller, e.g. by copying it into the outupt buffer using
memcpy(). This would signal to the compiler that nobody has access to the
buffer before you're done, and so it can optimise to its heart's content. But
now you're copying the data from one location to another just to tell the
compiler something about your intention. Copying is often expensive.
Or you could leave it to the function itself to allocate a buffer. This is
counter-intuitive for me: it's a heap allocation, which means work. When the
caller allocates the buffer, it can allocate on the stack, or at least amortise
a heap allocation across many invocations. Can returning something like a
std::vector really be the most efficient solution?
C tried to solve this problem, first with the noalias keyword (which was too
broad and too vague), then with restrict (which works for pointers but we
don't know how to generalise it for std::span and the like).
Perhaps the best solution is a special attribute (the first option above), but
with the caveat that it only applies to specific types for which the
semantics are specified, such as std::span. Or perhaps we just need a new
class to convey this special usage.
Range overlap
(April 2026)
Sometimes compilers can't produce optimal code because they don't know whether various loads and stores may conflict. If you're reading an array and writing to an array, it can be helpful for the optimiser to know whether there can be any overlap between the two. It will be able to reorder accesses in all sorts of efficient ways if there can't.
Language rules say that the compiler can safely make assumptions if the two arrays contain different types. But that only helps it ignore stupidity; it does nothing for the interesting cases.
An LLM suggested comparing pointers to check for overlap. The comparisons it suggested were wrong, but moreover, AFAIK pointer comparisons aren't even defined when the two aren't in the same allocation. You'd have to cast them first, and it all gets a bit ugly.
So perhaps it would be generally useful to have a standard library function which computes the overlap between two ranges.
Then, we could assure the compiler by stating:
[[assume(std::range_overlap(array1, array2).empty())]]
(And we could say the same in the function's precondition, since it's just a cheap one-off check.)
Dynamic-to-static calls
(April 2026)
Sometimes I run into situations where I need to pass a run-time value as a template argument.
So something like...
enum things { first, second, third };
void do_thing(thing t)
{
// Passing run-time variable as template argument! Shame.
call_doer<t>();
}
Why would I ever do that? Usually because I've got some performance-critical code that I want fully specialised, optimised, and inlined for each of the possible values. When I pass the run-time value to the compile-time parameter, I want to enter the corresponding one of those optimised code paths at that point, and perhaps never encounter that value as a run-time variable again.
(Or sometimes maybe it's just structural, where I might have different
specialisations of call_doer() for different values, and having the value as
a run-time parameter just makes for ugly code.)
So one way of expressing this in code might be a switch with an extra keyword
to signify that every code path inside of it is allowed to treat the switch
value as if it were constexpr:
void do_thing(thing t)
{
switch static (t)
{
default: call_doer<t>();
}
}
This implicitly generates separate code paths for the three different possible
thing values. The choice of syntax may be a bit confusing in that there is
effectively a switch in the generated code, but it's not necessarily exactly
the one that the C++ code suggests.
On the plus side, this syntax would leave us room for specialising more selectively for only some of the possible values, and punting others to a more generic code path:
Let's say your parameter is an int instead of an enum. You probably don't
want a separate, compile-time specialisation of call_doer() for each
possible value of an int!
I'd expect a diagnostic for that. What you'd probably do in practice would be to specialise for only a few of the possible values:
void do_thing(int i)
{
switch static (i)
{
case 0:
case 1:
case 2:
call_doer<i>(); break;
default:
call_generic_doer(i); break;
}
}
This would instantiate call_doer() for each of the values 0, 1, and 2,
calling the one corresponding to i; and for all other values it would call
call_generic_doer().
Limited friendship
(January 2022)
A lot of classes in libpqxx are friends. But friendship is a wide-open door.
Usually I'd prefer to specify, if I could, "class X gets friend access to
some of my members, but not all."
I devised a scheme called "gates" for this, which uses little intermediate classes that expose subsets of interfaces. But it's clunky, it's a pain to maintain, it probably sacrifices some performance, and ultimately it's not much clearer.
Now imagine a class could specify e.g...
class Gateway
{
public:
// ...
private:
// Used from inside the Gateway class, but also by a special Client class.
void log_activity(std::string_view);
// Class Client has the special privilege of calling log_activity(), but
// otherwise it's restricted to our public interface like everyone else.
friend class Client for { log_activity };
};
Template keyword arguments
(September 2020)
Wouldn't it be nice to pass template arguments by name sometimes, rather than positionally?
template<typename A=std::size_t, A begin=0, A end> void count();
count<end=10>();
You could fake most of this in the same way you can regular arguments, using a
constexpr parameters object with designated initializers:
count<{.end=10}>();
But... how do you pass the type parameter?
Aligned views and containers
(August 2020)
Some optimisations may be easier if views could specify alignment requirements
for their contents. For example, you might have a function taking a
std::string_view argument, that can run much faster if compiled for a
particular SIMD instruction set that operates on 32-byte blocks at a time.
That instruction set however may only operate on blocks of memory that are aligned to 32-byte boundaries. There can be a lot of overhead to accounting for strings that do not begin or end at 32-byte boundaries. Would it even be worth optimising? Most strings are short, so it might be very rare to find even a single aligned 32-byte block inside one.
But if the std::string_view template had a parameter to indicate alignment,
that could eliminate most of that overhead in cases where you can guarantee
appropriate alignment.
This would probably get easier if we had template keyword arguments. Who wants to specify an allocator just so they can pass a value for the next template parameter?
Generalised constexpr case labels
Would it be possible in general to allow case labels in switch statements
to carry a wider class of constexpr values?
It would be so nice to write...
switch (x)
{
case "one": return 1;
case "two": return 2;
case "three": return 3;
default: throw fail{"I don't know that number."};
}
Immutable strings
(January 2020)
The main C++ string class, std::string, is mutable to the extreme. You can
change their contents. They're basically dynamic buffers.
Some languages have immutable strings. There's pros and cons to both. For example, with immutable strings, an implementation could...
- merge ("intern") identical strings;
- use a more compact layout, without a separate size and capacity;
- hide the difference between static strings baked into the binary and dynamic ones formed at run time;
- optimise access more aggressively (because an immutable string can't change while you're reading it); and
- choose its own garbage collection strategies.
Also, sometimes immutable strings are just easier to reason about. Sure,
const will get you part of the way, but it doesn't guarantee that there's no
non-const access path to the same string that may break your assumptions.
So, it might be nice to have an immutable string class in addition to the
mutable one we have. Let's call it fixstr. One fun thing you'd be able to
do is move a std::string value into a fixstr — that would take over the
std::string's buffer and "freeze" it forever.
Kirit Sælensminde has built something like this. But
since it's not part of the standard library, you can't move a std::string's
buffer into the fixed string he's implemented.
Test Mode
(January 2018)
Okay, this is a bit of a crazy one.
Sometimes I want to break language rules in my tests:
- Temporarily override a member function in a class.
- Override a member function in an individual object.
- Access
privatemembers.
Is there some way this could be done, just for tests? It would allow me to
isolate components for more fine-grained unit testing, inject test doubles, and
set up white-box tests, all without compromising production code. Because
I don't want to make a production member function virtual just to accommodate
a test framework, or build a dangerous back door into my class that shouldn't
even exist as far as the actual product code is concerned.
I wouldn't mind if the code became agonisingly slow when I did this. In fact, all the better, so people won't be tempted to use these tricks in their actual production code to get around design problems.
Exposing members of members
To me, C++ inheritance is pretty much "default class members." Or maybe a special kind of inline namespaces.
It's also kind of overused. We do a lot with inheritance that could be done more cleanly with regular members, at the cost of some extra boilerplate:
class Parent
{
public:
void parent_func();
};
class Child
{
Parent parent;
public:
// Boilerplate: forward "parent" call to parent object.
void parent_func() { parent.parent_func(); }
void child_func() { /* ... */ }
};
That gives us something very similar to what we would get if Child simply
inherited from Parent.
Why have so much boilerplate though? Why not allow using to expose data
members' member functions?
// Boilerplate: forward "parent" call to parent object.
using parent::parent_func();
Function keyword
(May 2015)
C++ has a class keyword (among others) to introduce type declarations. But
function declarations are implicit: return_type name(parameter). There's
nothing there to say explicitly that this is a function. It even leads to some
annoying syntactical ambiguities.
Most modern languages have a keyword to make a function declaration explicit.
Mostly that makes the languages easier and faster to parse. That ship has
probably sailed for C++, but I think there are still other reasons for having a
keyword: there's no risk of ambiguity when you use it, and you get code that's
easier to search using generic text tools such as grep. Compilation could
perhaps be faster if you use it consistently. Syntax highlighting might also
be better.
Dotted member names
There's one small thing that bothers me about class members in C++. When I'm reading code in a member function, every unqualified identifier I see may raise the question: is this...
- a local value, or
- a class member, or
- a parent class member, or
- globally defined?
Since class data members can change value, that kind of makes them the new
global variables. Some of us like to add a conventional prefix (m_field) or
a suffix (field_), but we don't agree on what the convention should be.
Python has a nice solution for this. There is no implicit class member lookup.
To access a member variable field, a member function just always writes
self.field (the Python equivalent to this->field). It's not much more
verbose, but it does a lot for clarity.
So perhaps we could enforce the same in C++. I don't think we could just change our project standards and tooling to require it throughout, because sometimes we need to include third-party headers or even inherit from third-party classes.
But let's say we could prefix a member name with a dot to indicate that it can
only be referred to through an object pointer or reference, never implicitly
through this:
struct data
{
int .item;
void increment()
{
// NOT ALLOWED: Implicit member lookup.
++item;
// ALLOWED: Explicit "this" pointer.
++this->item;
}
};
As an extra feature, we could also allow the dotted name to stand on its own,
without the this part, as if the dot were simply part of the name.
That sometimes makes constructors nicer:
data(int item) : .item{item} {}
This leaves no ambiguity between .item (a member) and item (a parameter).
void variables
There are a few cases where I'd like to be able to create variables of type
void. I don't think they ever need their own storage or unique address, but
it eliminates a few annoying exceptional cases.
For example, in tests:
// Assert that func() throws an exception.
template<typename callable> void check_throws(callable const &func)
{
try
{
// If func() returns non-void and is [[nodiscard]]:
std::ignore = func();
// Otherwise:
func();
assert(false);
}
catch (const std::exception &)
{
// All good.
}
}
One version of the code only compiles for functions that return a non-void
value; the other will warn if the function is [[nodiscard]] which in some
situations will break the compile. (I like to set my options strict.)
I can hack up two version of the call depending on func's return type, in an
if constexpr. But it's ugly and verbose. If I could just assign
std::ignore to a void, there would be no problem.
Another fun thought is that conceptually, a std::set<X> could just be a
std::map<X, void> where the compiler could perhaps generate the same code.
Of course it's probably not so simple in practice. Should you be allowed to
pass a void value to a function that takes no arguments, since we've been
allowed to declare such a function with a (void) parameter list since C?
If so, should you be allowed to pass any number of extra void arguments to
a function? How would that affect template instantiation?
noexcept blocks
(Actually this one is so old that it originally talked about throw
specifications.)
Assume that your compiler or a static analysis tool will warn you when it
looks likely that a noexcept function may actually throw an exception.
Then it might be useful to mark individual code blocks as noexcept.
For example, in finicky code you can find yourself relying on a particular
short piece of code not throwing, but it's woven into the surrounding code to
a point where it doesn't abstract well into a separate function. Then it would
be nice to mark the code block as noexcept, and let your toolchain help guard
you against violations of this assumption.
Obsolete wishes
These are wishes I wrote down in the past which are no longer necessary:
for constexpr(August 2020): standardised astemplate forin C++26.- Object sync (2004): made moot by C++11 memory model.
thisas a reference: standardised in C++26.- Giving code in a
switchaccess to the switched value: possible in C++26. likelyandunlikely: standardised as attributes in C++20, though I envisioned them onboolvalues in general.- Cloning exceptions (2005): made moot by C++11
std::current_exception(). - Customised error messages (2004): obviated in C++11 by
static_assert()and deleted functions. prohibitedaccess level (2004): obviated by C++11 deleted functions.- Namespace inheritance (2004): made mostly pointless by C++11 inline namespaces.