advanced

C++17: The two line visitor explained--Marius Elvert

The power of C++17.

C++17: The two line visitor explained

by Marius Elvert

From the article:

If you have ever used an “idiomatic” C++ variant datatype like Boost.Variant or the new C++17 std::variant, you probably wished you could assemble a visitor to dispatch on the type by assembling a couple of lambda expressions like this:

auto my_visitor = visitor{
  [&](int value) { /* ... */ },
  [&](std::string const& value) { /* ... */ },
};

Quick Q: With “-fno-exceptions”, what happens with “new T”?

Quick A: The behaviour will likely stay the same.

Recently on SO:

With “-fno-exceptions”, what happens with “new T”?

The way I understand it, operator new is defined by libstdc++. If you now compile your own code with -fno-exceptions, you cannot catch any exceptions, but you will still be linking against the normal version of libstdc++, which does throw an exception.

So yes, new T will throw an exception, even with -fno-exception.

However, if you compiled libstdc++ with -fno-exception as well, things become different. Now, new T cannot throw an exception but, if I read the libstdc++ manual right it will call abort() instead.

It seems that, if you want your new T to return NULL on failure, the only way is to explicitely specify nothrow...

Quick Q: Does access control matter for deleted constructors?

Quick A: No

Recently on SO:

Does access control matter for deleted constructors?

Since it's overload resolution that makes the program ill-formed in this case, and not access specifiers (which are checked later), there is no difference in outcome. The compiler will always complain that a deleted function was picked.

But since the idiom before C++11 was "declare but not define a private copy c'tor to disable copying", I would consider it going along with the same idiom, and therefore favorable. You are using the "old slang" with some new language to describe the same thing, except better.

Guidelines For Rvalue References In APIs--Jonathan Müller

Everything you need to know.

Guidelines For Rvalue References In APIs

by Jonathan Müller

From the article:

I’ll be giving a talk at ACCU about when to use which pointer types and why.

While working on that I made some guidelines for rvalue references in interfaces which didn’t quite fit the talk, so I’m writing about them here.

When should you use rvalue references as function parameters?

When as return types?

What are ref-qualified member functions and when and how should you use them?

Let’s tackle it one by one...

The “unsigned for value range” antipattern--Arthur O’Dwyer

What do you think?

The “unsigned for value range” antipattern

by Arthur O’Dwyer

From the article:

Background: Signed Integers Are (Not Yet) Two’s Complement
At the WG21 committee meeting which is currently underway in Jacksonville, JF Bastien will be presenting a proposal to make C++’s int data type wrap around on overflow. That is, where today the expression INT_MAX + 1 has undefined behavior, JF would like to see that expression formally defined to come out equal to INT_MIN...

Selective argument application -- Krzysztof Ostrowski

Presentation of a technique that enables transparent interception of the selected arguments passed to any function by means of a user-provided predicate metafunction.

Selective argument application

by Krzysztof Ostrowski

From the article:

Presentation of a design of an interceptor abstraction built over C++17 utilities that records values of the selected arguments passed to any action transparently, then applies that action to the captured arguments, and eventually passes the result value to the next action.

Quick Q: What's the difference between std::move and std::forward?

Quick A: One is used to forward parameters, one to move an object.

Recently on SO:

What's the difference between std::move and std::forward?

std::move takes an object and allows you to treat it as a temporary (an rvalue). Although it isn't a semantic requirement, typically a function accepting a reference to an rvalue will invalidate it. When you see std::move, it indicates that the value of the object should not be used afterwards, but you can still assign a new value and continue using it.

std::forward has a single use case: to cast a templated function parameter (inside the function) to the value category (lvalue or rvalue) the caller used to pass it. This allows rvalue arguments to be passed on as rvalues, and lvalues to be passed on as lvalues, a scheme called "perfect forwarding."

To illustrate:

void overloaded( int const &arg ) { std::cout << "by lvalue\n"; }
void overloaded( int && arg ) { std::cout << "by rvalue\n"; }

template< typename t >
/* "t &&" with "t" being template param is special, and  adjusts "t" to be
   (for example) "int &" or non-ref "int" so std::forward knows what to do. */
void forwarding( t && arg ) {
    std::cout << "via std::forward: ";
    overloaded( std::forward< t >( arg ) );
    std::cout << "via std::move: ";
    overloaded( std::move( arg ) ); // conceptually this would invalidate arg
    std::cout << "by simple passing: ";
    overloaded( arg );
}

int main() {
    std::cout << "initial caller passes rvalue:\n";
    forwarding( 5 );
    std::cout << "initial caller passes lvalue:\n";
    int x = 5;
    forwarding( x );
}

As Howard mentions, there are also similarities as both these functions simply cast to reference type. But outside these specific use cases (which cover 99.9% of the usefulness of rvalue reference casts), you should use static_cast directly and write a good explanation of what you're doing.

Quick Q: How can a class template store either reference or value?

Quick A: This happen by default.

Recently on SO:

How can a class template store either reference or value?

You already wrote it (minus the required template <typename T>). The deduction rules for a forwarding reference preserve value category as follows:

  1. If t is bound to an lvalue of type T2, then T = T2&.
  2. If t is bound to an rvalue of type T2, then T = T2.

It's those deduction rules that std::forward relies on to do its job. And why we need to pass the type to it as well.

The above means that you instantiate holder directly with T2 in the rvalue case. Giving you exactly what you want. A copy is made.

As a matter of fact, two copies are made. Once to create the constructor argument t, and the other copy is to initialize obj_m from it. But we can get rid of it with some clever use of type_traits:

template <class T>
class holder {
    T  obj_m;  // should be a reference if possible...
public:
    holder(std::add_rvalue_reference_t<T> t) :obj_m { std::forward<T>(t) } {}
};

template<typename T>
auto hold_this(T && t) { return holder<T>(std::forward<T>(t)); }

See it live. We use add_rvalue_reference_t to make t be of the correct reference type in each case. And "simulate" the argument deduction which would make obj_m { std::forward<T>(t) } resolve to initializing obj_m from the correct reference type.

I say "simulate" because it's important to understand the constructor argument for holder cannot be a forwarding reference because the constructor itself is not templated.

By the way, since you tagged c++17, we can also add a deduction guide to your example. If we define it as follows (with the feedback from T.C. incorporated):

template <class T>
class holder {
    T  obj_m;  // should be a reference if possible...
public:
    holder(T&& t) :obj_m { std::forward<T>(t) } {}
};

template<typename T>
holder(T&&) -> holder<T>;

Then this live example shows you can define variables as hold h1{t}; and hold h2{test()};, with the same deduced types as the function return values from before.

Quick Q: Most concise way to disable copy and move semantics

Quick A: Delete the move assignment.

Recently on SO:

Most concise way to disable copy and move semantics

According to this chart (by Howard Hinnant):

The most concise way is to =delete move assignment operator (or move constructor, but it can cause problems mentioned in comments).

Though, in my opinion the most readable way is to =delete both copy constructor and copy assignment operator.