0

Recently I stumbled over this article on how to import dll libraries in modern C++. The code totally overwhelmed me, so I went through it line by line and tried to figure out its meaning. By now, I think I got it but one thing is still not clear to me:

class ShellApi {
  DllHelper _dll{"Shell32.dll"};

  /* ... */
};

class DllHelper {
public:
  explicit DllHelper(LPCTSTR filename) : _module(LoadLibrary(filename)) {}    
  /* ... */

private:
  HMODULE _module;
};

Why is the instantiation DllHelper _dll{"Shell32.dll"} written with curly brackets instead of normal ones? I tried it out in Visual Studio and had to realize that this snippet does not work with normal brackets. Why not? How is this kind of instantiation called (so I can look it up later)? Are there other scenarios where this is used?

If the code I provided is not enough to answer the question, the whole code is available in the article.

K. Krull
  • 185
  • 2
  • 8
  • 3
    I'd say that this (direct list initialization) is an example of normal initialization and the rest are legacy / C-ism ways. – user7860670 Aug 08 '19 at 11:10
  • Maybe that's [List initialization](https://stackoverflow.com/questions/18222926/why-is-list-initialization-using-curly-braces-better-than-the-alternatives)? – domsson Aug 08 '19 at 11:10
  • https://en.cppreference.com/w/cpp/language/list_initialization has an overview. – Shawn Aug 08 '19 at 11:11
  • See [this post](https://herbsutter.com/2013/05/09/gotw-1-solution/) about the different types of initialization and why some people may prefer to use `{}` by default in most cases. – jdehesa Aug 08 '19 at 11:17

1 Answers1

4

List-initialization exists since C++11 and is usually seen as the de-facto way to initialize object nowadays (except for some special cases):

std::string s{"foo"};  // Initialize the std::string s with "foo"

There are differences between list-initialization and direct-initialization that are summarized on https://en.cppreference.com/w/cpp/language/list_initialization, some of which are:

  • list-initialization does not allow narrowing conversions:
int f();
char c1(f()); // Ok
char c2{f()}; // error: narrowing conversion from int to char
struct X { };

X x1(X()); // x1 is a function
X x2{X{}}; // x2 is a X
  • in your case, list initialization is used to default-initialize a non-static member of ShellApi, and you cannot use () to initialize it:
struct X {
   int a1{1};   // Ok
   int a2 = 1;  // Ok
   int a3(1);   // Nok

   int b{};  // Ok, b is an int member default-initialized to 0
   int b();  // Ok, b is a member-function returning an int
};
Holt
  • 36,600
  • 7
  • 92
  • 139
  • Thanks! That means, an alternative (pre-C++11) way would be to default-initialize in the constructor: `ShellApi() : _dll("Shell32.dll") {}`? In that case, list-initialization would seem by far more readable, though that's personal preference, I guess. – K. Krull Aug 08 '19 at 21:22
  • @K.Krull Yes, but you would have to do this on all constructors. – Holt Aug 09 '19 at 15:53