emplace_back вызывает ошибку ссылки на статический член constexpr

Почему emplace_back ссылается на элемент, требующий определения? В чем разница между emplace_back(integer literal) и emplace_back(static constexpr integer member)?

Если я переключусь на С++ 17, он компилируется отлично. Я обнаружил, что в С++ 17 статические элементы данных constexpr неявно встроены. Означает ли это, что компилятор неявно создает для них определение?

Пример кода:

class base {
    int n;
public:
    base(int n):n(n) {}
};

struct base_trait {
    static constexpr int n = 1;
};

int main(void) {
    vector<base> v;
    v.emplace_back(1);  // ok
    v.emplace_back(base_trait::n);  // link error with -std=c++14, ok with -std=c++17
    return 0;
}

Ответы

Ответ 1

Как вы сказали, emplace_back принимает аргументы по ссылке, поэтому передача base_trait::n заставляет его использовать odr.

объект используется odr, если его значение считывается (если оно не является константой времени компиляции) или не записывается, его адрес берется или ссылка привязана к нему;

До С++ 17 это означает, что здесь требуется определение base_trait::n. Но так как С++ 17 изменилось поведение, для constexpr static data member определение вне класса не требуется снова.

Если элемент non-inline (since C++17) or a constexpr static data member (since C++11) используется нестандартно, определение в области пространства имен по-прежнему требуется, но оно не может иметь инициализатор. This definition is deprecated for constexpr data members (since C++17).

Статический элемент данных может быть объявлен как встроенный. Встроенный элемент статических данных может быть определен в определении класса и может указывать инициализатор. Это не требует определения вне класса. (поскольку С++ 17)