WinRT — нет статических конструкторов?

В настоящее время я пишу некоторые пользовательские элементы управления WinRT на c ++, и мой компилятор / intellisense говорит мне, что статические конструкторы недопустимы.

Мне нужно настроить некоторые статические данные, и я мог бы использовать частный флаг экземпляра bool, и при первом создании экземпляра моего класса я мог создавать статические данные и т. Д. (Эффективно достигая того же самого).

Однако, может быть, я что-то упустил, так как это кажется немного скучным.

Что такое канонический альтернативный подход к статическому построению в WinRT / c ++

Спасибо

3

Решение

Вы объявляете статические члены внутри класса, но вы должны определить их снаружи:

// In header file
class Foo
{
static int bar;
static int bar2;

static int init_bar3() { return 123; }
};

// In source file
int Foo::bar;

// Define and intiailize
int Foo::bar2 = 5;

// For more complicated initialization
int Foo::bar3 = Foo::init_bar3();
6

Другие решения

C ++ не поддерживает статические конструкторы. Большую часть времени вы должны использовать статическую инициализацию, как показано в Иоахим Пилеборг ответ. Я бы посоветовал вам адаптироваться к этому идиоматическому C ++ способу выполнения статической инициализации, а не пытаться писать C ++ в стиле C #.

Однако, если вам действительно нужен статический конструктор в стиле C # в C ++, вы можете подделать их:

class Foo {
public:
Foo() {
static_constructor();//call must appear in every constructor
}

Foo(Bar bar, Baz baz) {
static_constructor();//call must appear in every constructor
}

private:
static void static_constructor() {
static bool run = false;
if( !run ) {
run = true;
//your logic goes here
}
}
};
2

Как демонстрирует Йоахим Пилеборг в своем ответе, глобальная переменная может иметь динамический инициализатор, который может использоваться как форма «статического конструктора» в C ++ и C ++ / CX. Обратите внимание, что вы можете использовать лямбда-выражение, чтобы сохранить инициализацию локальной, например,

int Foo::bar = [](){ return 123; }();

Тем не менее, вам следует не делать этого, по возможности, по двум причинам. Во-первых, порядок, в котором глобальные переменные динамически инициализируются, указывается только частично. Если у вас есть динамический инициализатор, который зависит от других глобальных переменных, определенных в другом исходном файле, вы, вероятно, столкнетесь с проблемами.

Во-вторых, и что более важно, компонент среды выполнения Windows представляет собой DLL, и динамическая инициализация глобальных переменных происходит как часть инициализации DLL, которая происходит при вызове точки входа DLL. Как отмечает MSDN в документации DllMain, «Существуют серьезные ограничения на то, что вы можете сделать в точке входа DLL». Если вы будете искать в Интернете «DllMain», вы найдете множество ресурсов, описывающих проблемы, которые могут возникнуть из-за того, что вы делаете что-то захватывающее во время инициализации DLL.

В частности, вам запрещено делать что-либо, что может привести к загрузке другой библиотеки DLL. Это означает, что, как правило, вы не можете использовать — прямо или косвенно — любые типы среды выполнения Windows, которые не определены в вашей DLL, потому что библиотеки DLL, в которых они определены, возможно, еще не были загружены. Я отладил несколько зависаний, вызванных взаимоблокировками, вызванными экзотической работой, выполняемой во время динамической инициализации.

Итак, в качестве альтернативы, инкапсулируйте глобальные переменные в функцию …

class Foo {
public:
static int bar() {
static int value = [](){ return 123; }();
return value;
}
};

…и использовать Foo::bar() вместо Foo::bar, Статические переменные области блока инициализируются один раз, когда функция вводится впервые. Обратите внимание, что если вы используете Foo::bar из нескольких потоков вам может понадобиться синхронизировать инициализацию; C ++ 11 требует, чтобы инициализация была поточно-ориентированной, но Visual C ++ пока не поддерживает (начиная с Visual C ++ 2012) эту функцию C ++ 11.

1