Должен ли этот код не скомпилироваться в C++17?

Я обновлял проект для использования C++17 и обнаружил несколько случаев, когда код, который следовал этому шаблону, вызывал ошибку компиляции в последних версиях clang:

#include <boost/variant.hpp>

struct vis : public boost::static_visitor<void>
{
    void operator()(int) const { }
};

int main()
{
    boost::variant<int> v = 0;
    boost::apply_visitor(vis{}, v);
}

При использовании clang v8.0 в режиме C++17 это происходит со следующей ошибкой:

<source>:11:30: error: temporary of type 'boost::static_visitor<void>' has protected destructor
    boost::apply_visitor(vis{}, v);
                             ^
/opt/compiler-explorer/libs/boost_1_64_0/boost/variant/static_visitor.hpp:53:5: note: declared protected here
    ~static_visitor() = default;

Тем не менее, он компилируется чисто в режиме C++14. Я обнаружил, что если я изменю инициализацию скобки vis{} в скобках vis(), то он правильно компилируется в обоих режимах. Каждая пробная версия gcc допускает оба варианта в режиме C++17.

Это правильное изменение в поведении с C++14 на C++17, или это ошибка лягушки? Если это правильно, почему это теперь недопустимо в C++17 (или, возможно, так было всегда, но clang просто позволяет это в более ранних стандартных версиях)?

1 ответ

Решение

Лязг здесь прав. Вот сокращенный пример:

struct B {
protected:
    B() { }
};

struct D : B { };

auto d = D{};

В C++14 D не является агрегатом, потому что у него есть базовый класс, поэтому D{} это "нормальная" (неагрегированная) инициализация, которая вызывает Dконструктор по умолчанию, который в свою очередь вызывает Bконструктор по умолчанию. Это хорошо, потому что D имеет доступ к Bконструктор по умолчанию.

В C++17 определение агрегата было расширено - теперь разрешены базовые классы (если они неvirtual). D в настоящее время совокупность, что означает, что D{} это агрегатная инициализация. И в агрегатной инициализации это означает, что мы (вызывающая сторона) инициализируем все подобъекты - включая подобъект базового класса. Но у нас нет доступа к Bконструктор (это protected), поэтому мы не можем ссылаться на него, поэтому он плохо сформирован.


Не бойся, исправить это легко. Используйте скобки:

auto d = D();

Это восходит к вызову Dконструктор по умолчанию, как и раньше.

Другие вопросы по тегам