Есть ли способ сделать функцию-член НЕ вызываемой из конструктора? - PullRequest
21 голосов
/ 08 апреля 2019

У меня есть функция-член (метод), которая использует

std::enable_shared_from_this::weak_from_this() 

Короче: weak_from_this возвращает weak_ptr в это .Одно предостережение: его нельзя использовать из конструктора.Если бы кто-то использовал мою функцию из конструктора унаследованного класса, weak_from_this внутри него вернул бы expired weak_ptr.Я защищаюсь от этого с проверкой утверждений, что срок действия не истек, но это проверка во время выполнения.

Есть ли способ проверить это во время компиляции?

Ответы [ 3 ]

15 голосов
/ 08 апреля 2019

Боюсь, ответ: «Нет, от этого невозможно защититься во время компиляции». Доказать отрицание всегда сложно, но учтите следующее: если бы можно было защитить функцию таким способом, это, вероятно, было бы сделано для weak_from_this и shared_from_this в самой стандартной библиотеке.

5 голосов
/ 08 апреля 2019

Нет, нет пути. Рассмотрим:

void call_me(struct widget*);

struct widget : std::enable_shared_from_this<widget> {
    widget() {
        call_me(this);
    }

    void display() {
        shared_from_this();
    }
};

// later:

void call_me(widget* w) {
    w->display(); // crash
}

Дело в том, что есть причина, по которой вы хотите проверить, чтобы не вызывать shared_from_this в конструкторе. Подумай об этой причине. Дело не в том, что shared_from_this не может быть вызвано, а в том, что его возвращаемое значение еще не назначено. Это также не потому, что он никогда не будет назначен. Это потому, что он будет назначен позже при выполнении кода. Порядок работы является свойством среды выполнения вашей программы. Вы не можете утверждать во время компиляции порядок операций, который выполняется во время выполнения.

4 голосов
/ 08 апреля 2019

Не как таковой, но - если производительность не является проблемой, вы можете добавить флаг, который указывает, что строительство завершено, и использовать его для сбоя во время выполнения с такими вызовами:

class A {

    // ... whatever ...
public:
    A() { 
        // do construction work
        constructed = true;
    }

    foo() {
        if (not constructed)  { 
            throw std::logic_error("Cannot call foo() during construction"); 
        }
        // the rest of foo
    }

protected:
    bool constructed { false };
}

Вы также можете использовать эти проверки только при компиляции в режиме DEBUG (например, с условной компиляцией с использованием препроцессора - #ifndef NDEBUG), чтобы во время выполнения вы не получали потери производительности. Имейте в виду noexcept S хотя.

Альтернативой метанию может быть assert() 'ing.

...