简体   繁体   中英

When is passing the address of a global variable as a template argument useful?

I recently watched a video from Microsoft explaining some of the new features of C++17 and I was intrigued to find a feature of C++14 (and possibly even prior to that?) which allows you to use the address of a global variable as a constant expression for a template argument.

This allows code like the following:

#include <iostream>
int g_iTest = 5;

template <int* Addr>
struct S {
    static int TestAdd( int iTest ) {
         *Addr = iTest + *Addr;
         return *Addr;
    }
};

int main() {
    S<&g_iTest> s;
    std::cout << s.TestAdd( 5 ) << std::endl;
}

And this will result in an output of 10 . As it stands this is an amusing peculiarity of the language to me, however, the guy in the video stressed that this is a useful feature and has been used to reduce code bloat when passing around global variables. However, I don't understand how this works, as surely we could just have a header file with extern int g_iTest and then we can access the global variable without the extra level of abstraction?

What potential use-cases are there for this feature?

Well templating on pointers goes all the way back to C++98.

Templating on function pointers makes it possible to have a specified function as part of the type, and that's often useful.

Use cases for templating on addresses of global variables are more rare. I've only used it for some experimental code where the variables were pointers to strings. As I recall there was some problem with requirement of extern linkage, but that problem probably disappeared with C++11 (if it were there in the first place).

Sometimes you want to access global state.

There are a few ways to do so.

  1. You could have a static local variable.
  2. You could have an extern variable you reference.
  3. You could have a static global variable you reference.
  4. You could have callers pass a pointer/reference to the global state in (or function that gets it).
  5. Or you could pass it as a pointer to a global variable in the template argument list.

For each of the above, you can wrap the global state up into a function call, and have the function go get it for you. This just adds a level of indirection. (It also means it is possible to mix two of the above: function passed which uses a static local, or an extern linkage function that within its unit accesses static global state, etc).

What remains to show is that 5 has some advantages over each of the others.

4 requires explicit argument passing, which may cause noise. You may also have constrained signatures do to interop with another API. The fact that the global state is determined at compile time can also lead to some optimizations, and reduce the difficulty of reasoning about what the function does (as the pointer-to-state is one of a fixed set of values determined at compile time).

1 2 and 3 all bind which global state you are using to the code tightly -- the same piece of code cannot use different global state depending on how it is used.

5 allows one piece of code to use global state chosen elsewhere (including multiple different global states). This could allow easier mocking among other things.

A concrete example of this might be a template type or function that stores a pointer to a programmer assigned name. I could write a wrapper around a string-based API that provides me with string-less access to a property in that API by binding the name of the property into a class or function, and then having it consume the API.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM