103 comments

[ 3.7 ms ] story [ 175 ms ] thread
Variant, destructuring, template auto.
Yet memcpy(0, 0, 0) is still undefined behavior? For shame.
Please read the edit below. This case has a very subtly trap.

------ Old comment:

Makes sense it's undefined behavior, because address 0 is involved. Although I'd guess compiler implementations might just drop whole statement, because length is 0.

Zero length memory copy is a no-op.

Just like static zero iteration loop. They become zero instructions. (Except of course initialization that affects something outside loop scope.)

Edit:

Changed my mind after learning about the tricky UB pathway: If compiler can statically prove memcpy length argument to be zero, it can ALSO prove both argument pointers to be not NULL!

Ouch!

memcpy(0x1, 0x1, 0) is a well-defined no-op even when you don't have 0x1 in your address space.

Why handle 0x0 differently?

Well, just before I just saw a "read" and a "write" to address 0.

Not as mechanism that can cause serious bugs, when the compiler can statically prove length argument to be 0 and you can have NULL arguments as input.

Calls to memcpy with zero length are definitely not uncommon. Macros are an obvious way, but it's worse. All you need is a data flow where compiler can statically prove that all paths lead to zero length argument.

I have to agree it's very scary that memcpy(NULL, NULL, 0) is undefined.

Passing a non-null pointer that points outside your address space is made UB by the exact same sentence as the one that makes passing null UB.
> memcpy(0x1, 0x1, 0) is a well-defined no-op

Sure about that?

It really should be a no-op. The C and C++ standards are seriously broken in this respect, and compiler authors take advantage of this brokenness to get away with miscompiling programs that, to anyone with common sense, are fine.

It makes absolutely no sense for this behavior to be undefined, and letting it be undefined behavior causes real problems. See https://www.imperialviolet.org/2016/06/26/nonnull.html

The C and C++ standards must be changed to explicitly state that memory operations on zero bytes do nothing regardless of whether the inputs are valid pointers.

Ouch. Didn't think about that compilers can then assume passed pointers aren't NULL. And proceed to optimize your NULL checks away, because that memcpy would have been undefined behavior.

It seems so obvious after reading that. Should always ask oneself "what can the compiler prove as a consequence of this undefined behavior?".

So, you're right, memcpy(NULL, NULL, 0) should be defined as a no-op.

Some of us been trying to highlight for a long time now how compilers are taking unjustifiable liberties with undefined behavior
Well, undefined behavior is generally a good thing. It enables a lot of very reasonable optimizations.

Zero-length memcpy pathway to UB is not obvious, because whether the whole thing is UB depends on whether compiler can statically prove the length to be zero. Ugh.

I think the statically-prove-length-is-zero thing is a distraction. What do you think of memcpy(ptr1, ptr2, 1)? The compiler should be able to assume (legitimately, this time) that ptr1 and ptr2 are non-null. The problem isn't a special case for zero length: the problem is the lack of a special case for zero length.
Undefined behavior does not enable optimizations. Rather, the compiler may conclude that something must be true, because the alternative would be undefined behavior. Then, it may make optimization decisions based on that thing being true, causing potentially mysterious errors if, in fact, that thing is not true because the programmer made a mistake.

It's also possible you're confusing undefined behavior with implementation defined behavior.

memcpy behavior is not part of C++ standard. The C++ standard is only concerned with formally defining the ability to use it and the header name to include.
Strictly interpreting true, but grandparent's point is very valid.

It's used everywhere in real life C++ code.

quotemstr got a very good point and I'm grateful for the heads up.

but memcpy outside the C++ scope is what I meant. The C++ committee cannot do much about memcpy but they can introduce a new memcpy with a different name that has better ub support.
The C++ committee can and does change how functions of the C standard library work in C++, see Appendix C of the C++ Standard (granted, most of those changes simply specify how the library functions interact with C++ features).

C++ is not 100% compatible with C.

But the C++ standard could, without any changes to C, be amended to formally define which assumptions the compiler may or may not make about the C API in certain edge cases, such as memcpy(0,0,0).
memcpy is outside the territory of C++. it doesn't fall under the language specification. it's as simple as that. If they go this route what stops them from defining assumptions to system calls?
It will be great to see variants as a standard part of the language. Variants provide a way of doing a sort of type safe union for different types of data.
The new variant feature is based on boost::variant, which you can use today. Getting the thing into the standard library is nice and all, but it doesn't open up new capabilities the way that new syntax does.
The implementation of variant in C++17 is very different than in Boost. The Boost variant has a double buffer for the entire variant, and one of these buffers is always kept on the heap even if your variant is on the stack! This acts as a safety net incase the changing of the variant's value were to throw, you can get the old value back.

This implementation did not fly for C++'s goal of being high performance, and thus the new standardized variant is done quite differently.

I did not have a good experience with boost::variant ... Compile times increased drastically, compiler error messages became enormous and garbled with templates, syntax was onerous in places (e.g. "make_recursive_variant"), and identification of types is based on arbitrary index value that is risky to rely on (which() method). I ended up refactoring the whole thing, removing boost::variant and just using the traditional visitor pattern. The only advantage boost::variant really has over traditional C++ visitor pattern is that boost static_visitor functions can have a non-void return type ... Being able to directly return a value in the visitor functions is admittedly very convenient, but the other issues negate this convenience.
boost::variant is 12 years old, the std::variant version is not based on the actual design nor is it a copy of the boost implementation.

The interface is very similar, but the implementation is ofc using C++17 and not C++03.

The boost implementation is certainly showing its age at this point, as is mpl, now we have variadic templates.

Are there any C++14 variant implementations compatible with boost::variant and std::variant which are based on variadic templates? I'd love to move to a more modern implementation if possible.

My understanding is it is partly an enabling step to later introduce some sort of pattern matching along the lines of the work Bjarne had been doing so new syntax will likely come.
> but it doesn't open up new capabilities the way that new syntax does

Taking very normal so-what code and moving it into the standard is pretty much the point. Having variant in std:: lets new code use variants without adding a boost dependency. I say that without knocking on boost. Fewer dependencies are always better.

constexpr if seems pretty neat, previously you could do things like constexpr bool foo = /* some constexpr */, this makes it a bit easier and saves a line.

Structured bindings seem like one of those things that was just forgotten in previous standards, I'm glad it's here now. The syntax is a matter of taste, I guess.

I'm also quite excited about variants, they probably won't be used much but sometimes they're just what one needs. Having them without requiring boost is nice.

Can't you declare a bool as constexpr in C++14 (and maybe in 11)?
Eh yeah of course you can, I updated my comment. Thanks.
I agree that constexpr if looks great. I am confused about the decision on how to handle static_assert. It seems that

    if constexpr (false) { static_assert(false); }
Will fail to compile because the static_assert will trigger. This seems strange to me, anybody have an idea what the reasoning here is?
I think it's only consistent. Static assertions should always always always hold (things like type checks etc), if you really want to only evaluate it in that branch you can still do

    if constexpr (foo) {
        static_assert(!foo || /* assertion */);
    } else {
        static_assert(foo || /* assertion */);
    }
(this gets ugly fast with lots of else-ifs, but it's a workaround - I don't know why you'd ever need it, though)
Hmm, it seems this constexpr if is much more limited than I realized then. If I understood correctly the code in the the body of the constexpr if has to be correct even if the condition does not hold. I.e., this code:

    template<typename T> 
    void func(T x) {
        if constexpr ( std::is_same< T, ClassWithMemberF>::value ) {
            x.f();
        }
    }
will not work if you pass in a type for x that does not have a member function f. Is my understanding right? If so, constexpr if is not nearly as nice as I thought...
In my reading of it, this code would work. The code in if constexpr must be well-formed but does not have to be semantically valid. Your code is well-formed but not semantically valid, so this would be the perfect use for if constexpr
You may be correct, and I certainly hope you are. But if that's the case then the static_assert behavior seems all the more puzzling. In particular if this code works, then you can roll your own "static_assert" that will not trigger from the not taken branches.
constexpr isn't mean to replace the preprocessor. This will not compile for the same reason that

    if (false constexpr) { random gibberish }
will not compile, and that's a good thing. I think it would be odd to "hack" static_assert for this special case.
Opinions differ I guess. I would love it if

    if constexpr ( COND) { CODE };
would compile even if CODE only compiles when COND is true. There a lot of nice use cases for that, and that's how the static_if in D works.

You can still do this sort of stuff with template overloads, but it's much more cumbersome.

That's the case though, as long as CODE is syntactically correct. So this would compile:

  if constexpr (condition) {
    print("yes");
  }
While this wouldn't:

  if constexpr (condition) {
    print({
  }
At least that's my understanding.
Since the intended use of constexpr if is to block template instantiations, static_assert is kind of orthogonal to that. static_asserts in non-instantiated templates are ignored, as you would expect.
You are saying that static_assert would not trigger here, correct? That makes sense to me, but it's not what is in the proposal [1] - see the "Not proposed" section.

[1] http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p029...

No, I'm saying static_assert in non-instantiated templates would not trigger.

  template<class T>
  class foo;

  template<>
  class foo<int> {
    static_assert(false, "bad");
  };

  template<>
  class foo<float> {};

  template<class T>
  foo<T> foo_fn(T v) {
    if constexpr (std::is_same<T, int>::value) {
      return foo<int>{};
    }
    else if constexpr (std::is_same<T, float>::value) {
      return foo<float>{};
    }
    else {
      return foo<T>{};
    }
  }
  foo_fn(1.0); // <-- does not trigger static assert
  foo_fn(1); // <-- triggers static assert
This is consistent with the wording of the proposal.
This does not compile:

  template<>
  class foo<int> {
    static_assert(false, "bad");
  };
Reason: "false" is not a dependent name, so the compiler is allowed to check the static_assert when the template is being declared.

(Of course since this is a full specialization, there are no dependent names.)

See C++14 Standard §14.6.2 (Dependent Names) or http://en.cppreference.com/w/cpp/language/dependent_name (not sure how accurate that page is, though).

P.S: It might compile with Microsoft's Visual Studio, but only because that compiler doesn't implement two-phase name lookup, effectively turning every name into a dependent name.

Oh tricky! I did write that code on my phone so hopefully you'll excuse the sloppiness. Thanks to HN I have miraculously learned a new thing about C++ when I was sure I knew it all; though, not surprised.

Also this dependent name behavior of static_assert seems to be a precedent for always checking static_assert in constexpr if.

(comment deleted)
I'm very disappointed the networking ts wasn't included (I must have missed when that stopped being a C++ 17 thing). It's one of the few things missing that would let me use C++ for more projects (yes I know I can just include the boost library since that's essentially what it'll be but that's not always the easiest thing to grab and build into an existing project (at least not for a C++ novice like myself) but being part of the standard will make it immensely more accessible).

At least it still exists much like some of the other ones like the filesystem ts. http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/n458...

I'm not sure that you should be writing network code in C++ (which is a notorious source of nasty security bugs) if you're not comfortable enough with C++ to link against one of its most popular freely-available libraries.

I'd rather the committee focus on longstanding language core issues than add everyone's favorite library.

If you're in video game or embedded system, it's a different story.

Libraries are fine, but the problem here might be about OS or platform.

I had to write a plug in that makes a REST call a couple of weeks ago. This thing was ~200 LOC max and I had to link to a library that is orders of magnitude larger just to make an HTTP call. I would have loved some standard networking functionality in that instance.
The point was that you have to pull in third party library; LOC has nothing to do with it. I know all about libcurl. I work on an FDA regulated product; do you know what it takes for me to get third party code into our system?
Meh, I used to use C++ about 10 years ago and very much enjoyed it but the lack of multiple, built-in and cross-platform ways of doing things, like networking that higher level languages had I just simply stopped using it. This would give me an excuse to check it out again because, being part of the STL, it would be trivial to include in a project.

If I have a reason to use the language I can learn the build tooling (which last time I used C++ they were very, very old and overly complex IMO; nothing easy like rust's).

If you want to drive adoption of C++ you need to lower friction to using it. This works in every single thing ever created. If you can get more features into the STL that the vast, vast majority of languages have built in and get good tooling you'll have a very easy way for someone to jump into the language.

Alternatively you can continue to keep the friction high and address "language core issues" instead of making the STL have parity with all other popular languages. I think that approach is terrible but only if your goal is to grow the community of developers. But that's just my opinion of someone who loved C++ 10 years ago but had to move to higher level languages just to get stuff done, quickly and efficiently.

Why not just use boost.asio? It even supports coroutines now.
Using sockets is very portable imo. If I remember correctly there's only 2 functions that differ between linux and windows[1]. I'd rather use 2 source files than the whole boost asio/networking library.

[1] Those are ioctlsocket vs ioctl, and closesocket vs close. A basic TCP implementation can be cross platform with as little as 2 #ifdef/#endif blocks.

Only if you are sticking to synchronous IO. With nonblocking and async IO it gets much more complicated.
You should have a look at the Conan package manager. It'll allow you to use Boost (and other libraries) rather easily in your project.
I'm a bit frightened with the move toward adding a compile-time sublanguage. Frightened because I feel it will end up being half-baked and then abandoned like other C++ features (virtual inheritance for example).

As an example, I've been exploring Sean Parent's idea of concept-based polymorphism that allows you to implement "virtual functions" that get bound at compile time. To make this scheme explicit, I need to assert in the base class that all my "derived" classes implement a method with a certain signature. Does C++ allow me to do this?

> adding a compile-time sublanguage.

The C++ template system is already a Turing-complete functional programming language and has been since C++'s inception. I see nothing wrong with making this language's syntax more convenient.

> half-baked and then abandoned like other C++ features (virtual inheritance for example)

Virtual inheritance has its place. It's part of the language and works fine. What would you change?

(comment deleted)
Virtual inheritance means you have a diamond pattern and haven't separated your components properly. It should have never made it into the language as far as I'm concerned.
It's useful for interfaces, e.g. virtual public IFoo, is it not?
Interfaces should have been compile time, but for reasons I don't know, they weren't implemented as such
What would that mean? No dynamic dispatch at all?
It's possible to perform compile time de-virtualization. Half the time virtual is used to enforce inheritance rather than virtual dispatch.
> Virtual inheritance means you have a diamond pattern and haven't separated your components properly.

I've seen this argument pop out time and again as if it was a mantra of sorts, and more often than not it comes from someone with a background almost exclusively founded on Java.

Their line of reasoning essentially boils down to "Java doesn't support it, the people behind Java said something about it, therefore it's bad".

But C++ isn't Java, nor does Java dictate what is correct or what makes sense.

Particularly when the only assertion you could come up with to criticize multiple inheritance was a comment that had nothing to do with C++ or even OO programming, but only to do with your personal taste regarding your superficial impressions regarding software design.

Ooo, that Java remark burns. I've only touched Java seriously in the last few years of my career, I'm a hardcore C++/native type.

Here's the issues I take with virtual inheritance:

1. It complicates the vtable and function/member calls leading to another level of indirection, so from a performance perspective it's a negative mark.

2. The more fundamental problem is that you've botched the hasA vs isA association. If you have two classes that share a base class then that base class should be refactored into a component that can then be used as a member without polluting the class hierarchy. This leads to better composability since you can now pass this object around without pulling a whole class hierarchy with it.

You're welcome to use virtual inheritance all you want in your projects but much like knowing what subset of C++ to use(and what not) you'll never see it in code I work on.

> you've botched the hasA vs isA association

No you haven't.

In many senses, C++ inheritance is just shorthand for composition. That's why things like private inheritance are useful. Through this lens, virtual inheritance is automatic composition in which multiple composed objects each have a pointer to some shared object (the virtual base). It doesn't mess with hasA vs. isA at all.

> knowing what subset of C++ to use

This idea that a good coding standard necessarily bans parts of C++ has done massive damage to the C++ community. The correct subset of C++ to use is C++. Turning off parts of the language is just a cheap way to look sagacious while infantilizing developers. Every feature has its place.

I think we're just going to have to agree to disagree here.

There's a been a fair number of projects I've worked on where exceptions and RTTI introduced too much memory/perf overhead so we explicitly didn't use them. I don't feel like we were worse off for not having them.

I prefer not to work on projects that ban exceptions and RTTI. These features (especially exceptions) are important parts of the language, and without them, you can't reliably use most of the standard library (see bad_alloc) and can't really deliver value semantics, since you need awful hacks like two-phase initialization to communicate failure.

C++-without-exceptions is a very different and much worse language.

If you're interested in limiting your market, there's a whole embedded and high performance space where this is critical.

Considering LLVM takes the same stance[1] and they're the ones implementing language features that's good enough for me.

[1] http://llvm.org/docs/CodingStandards.html#do-not-use-rtti-or...

> If you're interested in limiting your market, there's a whole embedded and high performance space where this is critical.

Well, it's a preference, not a hard requirement. I'll hold my nose and work on such a codebase if there are other reasons, like the project itself being very interesting.

> Considering LLVM takes the same stance

IMHO, that's a mistake. But LLVM is an example of a project that's compelling enough to work on despite what I consider a set of poor language choices.

>I need to assert in the base class that all my "derived" classes implement a method with a certain signature. Does C++ allow me to do this?

Of course (C++14, possibly C++11):

  #include <type_traits>
  
  template <typename T, typename... Params>
  struct HasFoo {
      // SFINAE will disable this function template if U has no method foo(float)
      template <typename U>
      static constexpr bool check(decltype(std::declval<U>().foo(std::declval<Params>()...))*)
      {
          // We don't check foo's return type here
          return true;
      }
      
      // Fallback if above function template is disabled
      template <typename U>
      static constexpr bool check(...)
      {
          return false;
      }
      
      static constexpr bool value = check<T>(0);
  };
  
  template <typename D>
  struct Base {
      Base()
      {
          static_assert(HasFoo<D, float>::value, "D must implement 'foo(float)'");
      }
  };
  
  struct Derived {
      void foo(int)
      {
      }
  };

Since this essentially simulates a method call, implicit conversions work, as does overloading "foo".

As you have to implement "HasFoo" for every method you check, a macro will come in handy, say MAKE_METHOD_CHECK(Foo, bool, float)

I added some comments, feel free to ask if something is not clear :-)

Nice! Now to make the idea of compile time virtual functions real, can you force derived classes to have foo_impl, and have an inlined function foo in the base class that calls foo_impl? What about grand children classes?
>can you force derived classes to have foo_impl, and have an inlined function foo in the base class that calls foo_impl?

If that's all you want just write:

  void foo()
  {
    static_cast<Derived&>(*this).foo_impl();
  }
When the derived class doesn't have any method "foo_impl" (or no "foo_impl" with a suitable signature), you will get a compile-time error.

No need for the static_assert, that was just because KKKKkkkk1 asked whether checking for the existence of a method at compile-time was possible.

Compile-time polymorphism is essentially duck typing: If it quacks like a duck you can treat it as one. If it doesn't quack like a duck, but you still treat it as one you get a compiler error.

The technique for static polymorphism is known as CRTP in C++
"To make this scheme explicit, I need to assert in the base class that all my "derived" classes implement a method with a certain signature. Does C++ allow me to do this?"

As the call is resolved at compile time, simply calling the function will cause a compile error if the derived class doesn't implement it. The error message might not be the prettiest, but there are ways to improve that.

Really looking forward to auto templates, so annoying to have to specify type signatures in lambdas when most other languages don't require it.
Lambdas support that in C++14 (generic lambdas/polymorphic lambdas).
Ah shoot, you're right. Not sure how I got them confused.
I'm always happier the more I see old cruft removed in a new C++ standard.

Trigraphs [1] are gone. Good riddance, they're just used for pointless pranks anyways. Otherwise they just seem to cause issues, there's always someone who didn't know about them at all.

Also "unexpected_ownership_move" err... "auto_ptr" is gone [2]. Yeah, I know, there's nothing inherently wrong with it and you knew how to use it correctly...

[1]: http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2014/n398...

[2]: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n419...

Removing these facilities is a stunt. Real compilers will have to support them for decades (maybe at least auto_ptr) anyway, so there's no sense deleting them from the standard. It's sufficient to just recommend against their use.
I wouldn't say it's just a stunt. While they will remain supported, their removal from the standard can be cited by engineers for political support to modernize code bases. Compilers also support strict compilation modes which remove obsolete functions, to help modernize your code base. This is all facilitated by removal from the standard.

This also makes the standard smaller.

>While they will remain supported, their removal from the standard can be cited by engineers for political support to modernize code bases

Man... Who do you work for? If I used that as a reason for a refactoring my boss would laugh at me (and he's technical) . I imagine his boss would glaze over for a bit and then ask him to leave his office.

I think this casts my explanation in a very ungenerous light, I'm not really such an absurd person. Think of how a reasonable engineer would do it.

Speaking as an engineer, some of us have a tendency to want to refactor and polish code past the point where we should ship it and move on to something else. Management also has a tendency to view refactoring as unproductive and hurtful to the company's bottom line. Both are very reasonable positions. In most cases, the best strategy tends to be in the middle somewhere.

The auto_ptr class is gone from C++17 because it is error-prone to use it and there are better alternatives. However, I don't need to explain the intricacies of auto_ptr to my boss. I can just say something like, "We want to keep up with the latest industry best practices and standards. Some simple changes to our code base will reduce the chance for bugs and reduce support costs, and make it easier to develop new features."

The fact that auto_ptr has been removed from the standard entirely sends a stronger signal that you really should stop using it. Which is a good signal to send.

If you set -std=c++11 or some other version of the standard, they'll be there forever.
Trigraphs are actually incredibly annoying, because they silently change the meaning of characters (including in string literals). This is especially a problem when you use code generators.

It's why most C++ compilers these days don't even enable them by default. Kicking them out of the Standard is merely the recognition of that practice.

Wiki[0] says that IBM opposed the removal of trigraphs. I wonder why?

[0] https://en.wikipedia.org/wiki/Digraphs_and_trigraphs#Removal...

Because they still wanted to support some EBCDIC code pages which don't include { or }.

See also: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2009/n291...

Is the title in the title bar related to that article? It seems like a hold-over
Thanks for a terrifying read! I shudder to think what sort of (now essentially solved) problems the engineers on those teams must have to deal with. I mean, if their code page doesn't even support [, {, }, or ], then imagine what must happen when someone tries to enter a non-english name into their system!

It's remarkable that such systems still exist (and are apparently being updated, without their foundational cracks fixed) still today!

And it's patently absurd to consider their argument that 1) they're still using trigraphs, but 2) intend to build against the C++17 spec.

FFS, if you somehow find both of these to be true, preprocess your source to convert trigraphs to the corresponding glyphs, and to a character encoding that supports them. I can't possibly believe that they're running a C++17 compiler on a system that has no support for such a character set.

Because they still sell mainframes to customers, that pay them for keeping them running as they always did.
Modules didn't make it in but are arguably top of the list of practical improvements for everyone and considering the time it will take to trickle down to toolchains and build scripts.
To clarify a question raised in this write-up:

"auto [a , b , c] = getvalues();

"The braces are needed, getvalues returns a tuple. std::pair is not mentioned in the proposal, so its unclear if this works with pair, which is returned by the STL in some insert methods."

Destructured bindings will work with arrays; plain aggregate structs; and any class that offers a specialization of std::get<>(), so both std::tuple and std::pair are supported as a consequence. (C++11 added std::get<>() for std::pair).

What if the number of lhs values don't match the number of rhs values?
Compile-time error:

Otherwise, if the expression std::tuple_size<E>::value is a well-formed integral constant expression, the number of elements in the identifier-list shall be equal to the value of that expression

(E is the return type of "getvalues" in squidbidness's example, the initializer-list is "a, b, c".)