Порядок инициализации глобальной переменной - PullRequest
0 голосов
/ 18 декабря 2018

Одной из проблем глобальных переменных является то, что порядок инициализации не определен для всех единиц перевода, и у нас есть несколько способов избежать глобальных переменных.Но я все еще хочу понять порядок инициализации глобальных переменных в единицах перевода, просто для образовательных целей.

Предположим, у нас есть такой код:

action_type.h

struct ActionType {
    static const ActionType addShard;  // struct static variables
}

action_type.cpp

ActionType ActionType::addShard(addShardValue); 

action_set.h

ActionSet(ActionType s);

my.cpp:

// global variables

ActionSet s(ActionType::addShard);

Мои вопросы:

  1. Могу ли я всегда получить точное значение из глобальной переменной "s"?s зависит от ActionType :: addShard, который определен в другом модуле перевода.
  2. Если это не гарантировано, как я могу скомпилировать / связать / запустить, чтобы получить неправильный результат?Я слышал, что порядок зависит от стадии ссылки.

==== Чтобы облегчить обсуждение темы 2, вот мой тестовый код ====

//  cat action.h 

#ifndef ACTION_H
#define ACTION_H
#include <iostream>
#include <bitset>
namespace m {
    class ActionSet {
    public:
        ActionSet();
        ActionSet(std::initializer_list<int> ids);
        void dump() const;

    private:
        std::bitset<4> _actions;
    };
}
#endif /* ACTION_H */

// action.cpp

#include "action.h"
#include <iostream>

namespace m {
ActionSet::ActionSet(): _actions(0) {
    std::cout << "from default" << std::endl;
}
ActionSet::ActionSet(std::initializer_list<int> ids) {
    std::cout << "from init list.." << std::endl;
    for(auto id : ids) {
        _actions.set(id, true);
    }
}

void ActionSet::dump() const {
    for(int i=0; i<4; i++) {
        std::cout << _actions[i] << ",";
    }
    std::cout << std::endl;
}
}

// const.h

#ifndef CONST_H
#define CONST_H
namespace m {
struct X {
    static int x;
    static int y;
};
}

#endif /* CONST_H */

// const.cpp

#include "const.h"

namespace m {
    int X::x = 0;
    int X::y = 2;
};

// f.h  

#ifndef F_H
#define F_H

#include "action.h"
#include <iostream>

namespace m {
 void f1();
void f2();
}

#endif /* F_H */

// f.cpp
#include "f.h"
#include "const.h"

namespace m {
    const ActionSet s{X::x, X::y};

    void f1() {
        s.dump();
    }


    void f2() {
        const ActionSet s2{X::x, X::y};
        s2.dump();
    }
};

// action.h 

#ifndef ACTION_H
#define ACTION_H
#include <iostream>
#include <bitset>
namespace m {
    class ActionSet {
    public:
        ActionSet();
        ActionSet(std::initializer_list<int> ids);
        void dump() const;

    private:
        std::bitset<4> _actions;
    };
}
#endif /* ACTION_H */

// action.cpp

#include "action.h"
#include <iostream>

namespace m {
ActionSet::ActionSet(): _actions(0) {
    std::cout << "from default" << std::endl;
}
ActionSet::ActionSet(std::initializer_list<int> ids) {
    std::cout << "from init list.." << std::endl;
    for(auto id : ids) {
        _actions.set(id, true);
    }
}

void ActionSet::dump() const {
    for(int i=0; i<4; i++) {
        std::cout << _actions[i] << ",";
    }
    std::cout << std::endl;
}
}

// main.cpp

#include "f.h"


int main(int argc, char *argv[])
{
    m::f1();
    m::f2();
    return 0;
}

// CMakeLists.txt

cmake_minimum_required(VERSION 2.6)
project(project_name)
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD_REQUIRED on)
set(CMAKE_CXX_EXTENSIONS off)
set(CMAKE_EXPORT_COMPILE_COMMANDS on)
set( CMAKE_VERBOSE_MAKEFILE on )

add_executable(main const.cpp main.cpp f.cpp action.cpp)
add_executable(main2 main.cpp f.cpp action.cpp const.cpp)

1 Ответ

0 голосов
/ 18 декабря 2018

Вам нужно пройти через большой код, к сожалению, я не могу выяснить, что на самом деле является ActionType.

Как вы указали, использование глобальных переменных действительно плохая идея.К счастью, они добавили constexpr к языку.С constexpr вы можете создавать константы, которые «определяются» во время компиляции, не оказывая влияния на время выполнения.Таким образом, независимо от того, в каком порядке выполняются ваши Ctors, он даст правильный результат.

С другой стороны, std::initializer_list не является constexpr (даже в C ++ 20), std::bitset is.

#include <bitset>

struct ActionType {
    static constexpr std::bitset<4> addShard{0b0101};
};

Используя приведенный выше код, вы создали переменную constexpr, которую можно безопасно использовать для инициализации глобальной переменной.Точно так же вы можете создать свой следующий тип как constexpr доступны:

class ActionSet {
public:
    constexpr ActionSet();
    ActionSet(std::initializer_list<int> ids);
    constexpr ActionSet(std::bitset<4> actions) : _actions{actions} {}
    void dump() const;

private:
    std::bitset<4> _actions{0};
};

constexpr ActionSet s(ActionType::addShard);

Короче говоря, до тех пор, пока вы можете «определить» все во время компиляции (включая весь необходимый код в заголовках),Вы можете создавать константы на основе других констант.Вызов постоянных методов для них может быть выполнен позже во время выполнения.

Начиная с C ++ 20, вы должны иметь возможность написать:

[[constinit]] ActionSet s(ActionType::addShard);

Это должно позволить вам использовать не-const методы в вашей программе.Для меня неясно, позволяет ли это по-прежнему использовать 's' в конструкторе следующей переменной constexpr.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...