Asked  7 Months ago    Answers:  5   Viewed   9 times

At the 2016 Oulu ISO C++ Standards meeting, a proposal called Inline Variables was voted into C++17 by the standards committee.

In layman's terms, what are inline variables, how do they work and what are they useful for? How should inline variables be declared, defined and used?

 Answers

95

The first sentence of the proposal:

The ?inline specifier can be applied to variables as well as to functions.

The ¹guaranteed effect of inline as applied to a function, is to allow the function to be defined identically, with external linkage, in multiple translation units. For the in-practice that means defining the function in a header, that can be included in multiple translation units. The proposal extends this possibility to variables.

So, in practical terms the (now accepted) proposal allows you to use the inline keyword to define an external linkage const namespace scope variable, or any static class data member, in a header file, so that the multiple definitions that result when that header is included in multiple translation units are OK with the linker – it just chooses one of them.

Up until and including C++14 the internal machinery for this has been there, in order to support static variables in class templates, but there was no convenient way to use that machinery. One had to resort to tricks like

template< class Dummy >
struct Kath_
{
    static std::string const hi;
};

template< class Dummy >
std::string const Kath_<Dummy>::hi = "Zzzzz...";

using Kath = Kath_<void>;    // Allows you to write `Kath::hi`.

From C++17 and onwards I believe one can write just

struct Kath
{
    static std::string const hi;
};

inline std::string const Kath::hi = "Zzzzz...";    // Simpler!

… in a header file.

The proposal includes the wording

?An inline static data member can be defined in the class definition and may s??pecify a ?brace­-or­-equal­-initializer. If the member is declared with the constexpr specifier, it may be redeclared in namespace scope with no initializer (this usage is deprecated? see?? D.X). Declarations of other static data members shall not specify a ?brace­-or­-equal­-in??itializer

… which allows the above to be further simplified to just

struct Kath
{
    static inline std::string const hi = "Zzzzz...";    // Simplest!
};

… as noted by T.C in a comment to this answer.

Also, the ?constexpr? specifier implies  inline for static data members as well as functions.


Notes:
¹ For a function inline also has a hinting effect about optimization, that the compiler should prefer to replace calls of this function with direct substitution of the function's machine code. This hinting can be ignored.

Tuesday, June 1, 2021
 
Pradip
answered 7 Months ago
55

You are defining $arr = 'arr'; and then getting the second character from the string 'arr', not the array inside class Foo, that is why you are getting 'r' ([1] returning the second character from your word).

The solution? you should replace:

echo $fooo->$arr[1] . "n";

with:

echo $fooo->arr[1] . "n";

You should receive your desired output:

'I am B.'
Wednesday, March 31, 2021
 
Lloydworth
answered 9 Months ago
73

A closure is a pairing of:

  1. A function, and
  2. A reference to that function's outer scope (lexical environment)

A lexical environment is part of every execution context (stack frame) and is a map between identifiers (ie. local variable names) and values.

Every function in JavaScript maintains a reference to its outer lexical environment. This reference is used to configure the execution context created when a function is invoked. This reference enables code inside the function to "see" variables declared outside the function, regardless of when and where the function is called.

If a function was called by a function, which in turn was called by another function, then a chain of references to outer lexical environments is created. This chain is called the scope chain.

In the following code, inner forms a closure with the lexical environment of the execution context created when foo is invoked, closing over variable secret:

function foo() {
  const secret = Math.trunc(Math.random()*100)
  return function inner() {
    console.log(`The secret number is ${secret}.`)
  }
}
const f = foo() // `secret` is not directly accessible from outside `foo`
f() // The only way to retrieve `secret`, is to invoke `f`

In other words: in JavaScript, functions carry a reference to a private "box of state", to which only they (and any other functions declared within the same lexical environment) have access. This box of the state is invisible to the caller of the function, delivering an excellent mechanism for data-hiding and encapsulation.

And remember: functions in JavaScript can be passed around like variables (first-class functions), meaning these pairings of functionality and state can be passed around your program: similar to how you might pass an instance of a class around in C++.

If JavaScript did not have closures, then more states would have to be passed between functions explicitly, making parameter lists longer and code noisier.

So, if you want a function to always have access to a private piece of state, you can use a closure.

...and frequently we do want to associate the state with a function. For example, in Java or C++, when you add a private instance variable and a method to a class, you are associating state with functionality.

In C and most other common languages, after a function returns, all the local variables are no longer accessible because the stack-frame is destroyed. In JavaScript, if you declare a function within another function, then the local variables of the outer function can remain accessible after returning from it. In this way, in the code above, secret remains available to the function object inner, after it has been returned from foo.

Uses of Closures

Closures are useful whenever you need a private state associated with a function. This is a very common scenario - and remember: JavaScript did not have a class syntax until 2015, and it still does not have a private field syntax. Closures meet this need.

Private Instance Variables

In the following code, the function toString closes over the details of the car.

function Car(manufacturer, model, year, color) {
  return {
    toString() {
      return `${manufacturer} ${model} (${year}, ${color})`
    }
  }
}
const car = new Car('Aston Martin','V8 Vantage','2012','Quantum Silver')
console.log(car.toString())

Functional Programming

In the following code, the function inner closes over both fn and args.

function curry(fn) {
  const args = []
  return function inner(arg) {
    if(args.length === fn.length) return fn(...args)
    args.push(arg)
    return inner
  }
}

function add(a, b) {
  return a + b
}

const curriedAdd = curry(add)
console.log(curriedAdd(2)(3)()) // 5

Event-Oriented Programming

In the following code, function onClick closes over variable BACKGROUND_COLOR.

const $ = document.querySelector.bind(document)
const BACKGROUND_COLOR = 'rgba(200,200,242,1)'

function onClick() {
  $('body').style.background = BACKGROUND_COLOR
}

$('button').addEventListener('click', onClick)
<button>Set background color</button>

Modularization

In the following example, all the implementation details are hidden inside an immediately executed function expression. The functions tick and toString close over the private state and functions they need to complete their work. Closures have enabled us to modularise and encapsulate our code.

let namespace = {};

(function foo(n) {
  let numbers = []
  function format(n) {
    return Math.trunc(n)
  }
  function tick() {
    numbers.push(Math.random() * 100)
  }
  function toString() {
    return numbers.map(format)
  }
  n.counter = {
    tick,
    toString
  }
}(namespace))

const counter = namespace.counter
counter.tick()
counter.tick()
console.log(counter.toString())

Examples

Example 1

This example shows that the local variables are not copied in the closure: the closure maintains a reference to the original variables themselves. It is as though the stack-frame stays alive in memory even after the outer function exits.

function foo() {
  let x = 42
  let inner  = function() { console.log(x) }
  x = x+1
  return inner
}
var f = foo()
f() // logs 43

Example 2

In the following code, three methods log, increment, and update all close over the same lexical environment.

And every time createObject is called, a new execution context (stack frame) is created and a completely new variable x, and a new set of functions (log etc.) are created, that close over this new variable.

function createObject() {
  let x = 42;
  return {
    log() { console.log(x) },
    increment() { x++ },
    update(value) { x = value }
  }
}

const o = createObject()
o.increment()
o.log() // 43
o.update(5)
o.log() // 5
const p = createObject()
p.log() // 42

Example 3

If you are using variables declared using var, be careful you understand which variable you are closing over. Variables declared using var are hoisted. This is much less of a problem in modern JavaScript due to the introduction of let and const.

In the following code, each time around the loop, a new function inner is created, which closes over i. But because var i is hoisted outside the loop, all of these inner functions close over the same variable, meaning that the final value of i (3) is printed, three times.

function foo() {
  var result = []
  for (var i = 0; i < 3; i++) {
    result.push(function inner() { console.log(i) } )
  }
  return result
}

const result = foo()
// The following will print `3`, three times...
for (var i = 0; i < 3; i++) {
  result[i]() 
}

Final points:

  • Whenever a function is declared in JavaScript closure is created.
  • Returning a function from inside another function is the classic example of closure, because the state inside the outer function is implicitly available to the returned inner function, even after the outer function has completed execution.
  • Whenever you use eval() inside a function, a closure is used. The text you eval can reference local variables of the function, and in the non-strict mode, you can even create new local variables by using eval('var foo = …').
  • When you use new Function(…) (the Function constructor) inside a function, it does not close over its lexical environment: it closes over the global context instead. The new function cannot reference the local variables of the outer function.
  • A closure in JavaScript is like keeping a reference (NOT a copy) to the scope at the point of function declaration, which in turn keeps a reference to its outer scope, and so on, all the way to the global object at the top of the scope chain.
  • A closure is created when a function is declared; this closure is used to configure the execution context when the function is invoked.
  • A new set of local variables is created every time a function is called.

Links

  • Douglas Crockford's simulated private attributes and private methods for an object, using closures.
  • A great explanation of how closures can cause memory leaks in IE if you are not careful.
  • MDN documentation on JavaScript Closures.
Tuesday, June 1, 2021
 
Vlad
answered 7 Months ago
60

Indeed, C++20 unfortunately makes this code infinitely recursive.

Here's a reduced example:

struct F {
    /*implicit*/ F(int t_) : t(t_) {}

    // member: #1
    bool operator==(F const& o) const { return t == o.t; }

    // non-member: #2
    friend bool operator==(const int& y, const F& x) { return x == y; }

private:
    int t;
};

Let's just look at 42 == F{42}.

In C++17, we only had one candidate: the non-member candidate (#2), so we select that. Its body, x == y, itself only has one candidate: the member candidate (#1) which involves implicitly converting y into an F. And then that member candidate compares the two integer members and this is totally fine.

In C++20, the initial expression 42 == F{42} now has two candidates: both the non-member candidate (#2) as before and now also the reversed member candidate (#1 reversed). #2 is the better match - we exactly match both arguments instead of invoking a conversion, so it's selected.

Now, however, x == y now has two candidates: the member candidate again (#1), but also the reversed non-member candidate (#2 reversed). #2 is the better match again for the same reason that it was a better match before: no conversions necessary. So we evaluate y == x instead. Infinite recursion.

Non-reversed candidates are preferred to reversed candidates, but only as a tiebreaker. Better conversion sequence is always first.


Okay great, how can we fix it? The simplest option is removing the non-member candidate entirely:

struct F {
    /*implicit*/ F(int t_) : t(t_) {}

    bool operator==(F const& o) const { return t == o.t; }

private:
    int t;
};

42 == F{42} here evaluates as F{42}.operator==(42), which works fine.

If we want to keep the non-member candidate, we can add its reversed candidate explicitly:

struct F {
    /*implicit*/ F(int t_) : t(t_) {}
    bool operator==(F const& o) const { return t == o.t; }
    bool operator==(int i) const { return t == i; }
    friend bool operator==(const int& y, const F& x) { return x == y; }

private:
    int t;
};

This makes 42 == F{42} still choose the non-member candidate, but now x == y in the body there will prefer the member candidate, which then does the normal equality.

This last version can also remove the non-member candidate. The following also works without recursion for all test cases (and is how I would write comparisons in C++20 going forward):

struct F {
    /*implicit*/ F(int t_) : t(t_) {}
    bool operator==(F const& o) const { return t == o.t; }
    bool operator==(int i) const { return t == i; }

private:
    int t;
};
Sunday, August 8, 2021
 
Stubbi
answered 4 Months ago
22

Not in the scope of c++17, but c++20 onwards!

Yes. The proposal of consistent container erasure has been mentioned in n4009 paper and finally adopted in C++20 standard as std::erase_if which is a non-member function for each containers.

This ensures a uniform container erasure semantics for std::basic_string and all standard containers, except std::array(as it has the fixed-size).

This means that the boilerplate code

container.erase(
    std::remove_if(
        container.begin(), container.end(),
        [](const auto& element) ->bool { return /* condition */; }),
    vec.end());

will simply melt down to a generalized form of

std::erase_if(container, [](const auto& element) ->bool { return /* condition */; });

Secondly, this uniform syntax selects the proper semantics for each container. This means

  • For sequence containers like std::vector, std::deque and for std::std::basic_string, it will be equivalent to

    container.erase(
           std::remove_if(container.begin(), container.end(), unaryPredicate)
           , container.end()
    );
    
  • For sequence containers std::forward_list and std::list, it will be equivalent to

    container.remove_if(unaryPredicate);
    
  • For ordered associative containers(i.e. std::set, std::map, std::multiset and std::multimap) and unordered associative containers(i.e. std::unordered_set, std::unordered_map, std::unordered_multiset and std::unordered_multimap), the std::erase_if is equivalent to

    for (auto i = container.begin(), last = container.end(); i != last; ) 
    {
      if (unaryPredicate(*i)) 
      {
        i = container.erase(i);
      }
      else
      {
        ++i;
      }
    }
    

In addition to that, the standard also added std::erase for sequence containers of the form

std::erase(container, value_to_be_removed);
Thursday, August 12, 2021
 
brombeer
answered 4 Months ago
Only authorized users can answer the question. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :
 
Share