Использование значения по умолчанию для константного аргумента вызывает сбой - PullRequest
0 голосов
/ 06 июля 2018

У меня есть такой код:

struct Data {
    Data(const std::vector<int> &data = {}) : data_(data) {}
    const std::vector<int> &data_;
};

Data create1() {
    return Data(); // bad
}

Data create2() {
    return Data({}); // bad
}

Data create3(const std::vector<int> &data = {}) {
    return Data(data); // good
}

Data create4() {
    static const std::vector<int> data;
    return Data(data); // good
}

void main() {
    auto data1 = create1(); // deleted data_
    auto data2 = create2(); // deleted data_
    auto data3 = create3(); // ok
    auto data4 = create4(); // ok
}

Четыре функции создания кажутся мне одинаковыми. Но почему create1 и create2 вызывают удаленные данные_, но create3 и create4 - это нормально?

Ответы [ 2 ]

0 голосов
/ 06 июля 2018

Первые два случая довольно явные злоупотребления временными. Основываясь на комментариях автора, они получили то, что пошло не так в create1 и create2 на данный момент, поэтому давайте сосредоточимся на create3 и почему это работает.

Спойлер: Это не так.

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

struct test
{
    test()
    {
        std::cout << "test ctor\n";
    }
    ~test()
    {
        std::cout << "test dtor\n";
    }
};

Теперь мы делаем что-то похожее на Data и используем test вместо vector

struct Data {
    Data(const test &data = {}) : data_(data) 
    {
        std::cout << "data ctor\n";
    }
    ~Data()
    {
        std::cout << "data dtor\n";
    }
    const test &data_;

    void forceuse() // just to make sure the compiler doesn't get any bright ideas about 
                    // optimizing Data out before we're through with it.
    {
        std::cout << "Using data\n";
    }
};

И мы добавим немного дополнительной диагностики к create3 и снова заменим vector на test

Data create3(const test &data = {}) {
    std::cout << "in create3\n";
    return Data(data); // good
}

и сделать то же самое с main

int main() {
    { // change the scope of data3 so that we can put a breakpoint at the end of main 
      // and watch all of the fun
        std::cout << "calling create3\n";
        auto data3 = create3(); // ok
        std::cout << "returned from create3\n";
        data3.forceuse();
    }
}

Вывод этого

calling create3
test ctor
in create3
data ctor
test dtor
returned from create3
Using data
data dtor

test создается во время вызова create3 и уничтожается при выходе из create3. Это не живое в main. Если он кажется живым в main, это просто тупая неудача. Ваш друг и мое Неопределенное Поведение, будучи придурком. Опять же.

test создается до Data, но также уничтожается до Data, оставляя Data в плохом состоянии.

Вот приведенный выше код, все красиво собранные и запущенные в онлайн IDE: https://ideone.com/XY30XH

0 голосов
/ 06 июля 2018

Создать статический элемент данных и использовать его для инициализации:

struct Data {
    Data(const std::vector<int> &data = empty_) : data_(data) {}
    const std::vector<int> &data_;
private:
    static std::vector<int> empty_;
};

std::vector<int> Data::empty_ = {};

Data create1() {
    return Data(); // good
}

Data create2() {
    return Data({}); // still bad, as it should be
}

Data create3(const std::vector<int> &data = {}) {
    return Data(data); // good
}

Data create4() {
    static const std::vector<int> data;
    return Data(data); // good
}

void main() {
    auto data1 = create1(); // ok
    auto data2 = create2(); // deleted data_
    auto data3 = create3(); // ok
    auto data4 = create4(); // ok
}
...