Подтвердить что ты не робот

"Инициализатор элемента по умолчанию, необходимый для определения класса включения вне функций-членов" - мой код плохо сформирован?

#include <utility>

struct foo
{
    int x{0};
    foo() noexcept = default;
    void f() noexcept(noexcept(std::declval<foo&>())) {}
};

int main()
{ 
}

живой пример на godbolt


Код выше компилируется с любой версией g++, которую я тестировал, и с clang++ от 3.6 до 3.9.1, но не компилируется с помощью clang++ 4.0.0:

test.cpp:6:5: error: default member initializer for 'x' needed within 
definition of enclosing class 'foo' outside of member functions
    foo() noexcept = default;
    ^
type_traits:126:26: note: in instantiation of template 
class 'std::is_function<foo &>' requested here
    : public conditional<_B1::value, _B1, __or_<_B2, _B3, _Bn...>>::type
                        ^
type_traits:154:39: note: in instantiation of template 
class 'std::__or_<std::is_function<foo &>,
    std::is_reference<foo &>, std::is_void<foo &> >' requested here
    : public integral_constant<bool, !_Pp::value>
                                    ^
type_traits:598:14: note: in instantiation of template 
class 'std::__not_<std::__or_<std::is_function<foo &>,
    std::is_reference<foo &>, std::is_void<foo &> > >' requested here
    : public __not_<__or_<is_function<_Tp>, is_reference<_Tp>,
            ^
type_traits:121:26: note: in instantiation of template 
class 'std::is_object<foo &>' requested here
    : public conditional<_B1::value, _B1, _B2>::type
                        ^
type_traits:635:14: note: in instantiation of template 
class 'std::__or_<std::is_object<foo &>,
    std::is_reference<foo &> >' requested here
    : public __or_<is_object<_Tp>, is_reference<_Tp>>::type
            ^
type_traits:1667:33: note: in instantiation of template 
class 'std::__is_referenceable<foo &>' requested here
template<typename _Tp, bool = __is_referenceable<_Tp>::value>
                                ^
type_traits:1678:14: note: in instantiation of default 
argument for '__add_rvalue_reference_helper<foo &>'
    required here
    : public __add_rvalue_reference_helper<_Tp>
            ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
type_traits:2267:12: note: in instantiation of template 
class 'std::add_rvalue_reference<foo &>' requested
    here
    inline typename add_rvalue_reference<_Tp>::type
        ^
wtfff.cpp:7:32: note: while substituting explicitly-specified 
template arguments into function template 'declval'
    void f() noexcept(noexcept(std::declval<foo&>())) {}
                            ^
wtfff.cpp:5:9: note: default member initializer declared here
    int x{0};
        ^

Является ли мой код плохо сформированным? Если да, то какой смысл ошибки?

Обратите внимание, что удаление noexcept из конструктора или инициализатора {0} из x сделает компиляцию кода.

4b9b3361

Ответ 1

Ваш код в порядке, что я могу сказать. Кажется, что Clang работает с конструктором = default, а не просто определяет конструктор по умолчанию вручную. В его исходном коде есть следующий фрагмент:

DR1351:   Если элемент с несимметричным элементом данных, связанный с фигурной скобкой или равным   вызывает стандартный конструктор по умолчанию своего класса или   охватывающий класс в потенциально оцениваемом подвыражении,   программа плохо сформирована.

Эта резолюция неработоспособна: спецификация исключения конструктор по умолчанию может потребоваться в неоценимом контексте, в в частности, в операнде noexcept-expression, и мы можем не удалось вычислить спецификацию исключения для закрытого класса.

Любая попытка разрешить спецификацию исключения по умолчанию по умолчанию конструктор до того, как инициализатор будет лексически завершен, в конечном итоге приходите сюда, в этот момент мы можем его диагностировать.

Я думаю, что это может неправильно ошибиться, лично. Но он конкретно упоминает "дефолтный конструктор по умолчанию".

Кажется, что работает следующее:

#include <utility>

struct foo
{
    int x{0};
    foo() noexcept {} // = default;
    void f() noexcept(noexcept(std::declval<foo&>())) {}
};

int main()
{ 
}

Ответ 2

Ваше использование в порядке.

Что осталось? Я думаю, ошибка компилятора. Как насчет этого для доказательства? Используйте z только z вместо foo в declval.

#include <utility>
struct z{};
struct foo
{
    int x{0};
    foo() noexcept = default;
    void f() noexcept( noexcept( std::declval<z>() ) ) {}
};

int main()
{ 
}

Clang 4.0.0 на godbolt все еще ошибки одинаковы. К сожалению, у меня нет clang 4.0.0, доступного на машине для тестирования, поэтому я не могу сказать, является ли это Clang или godbolt наверняка.

Ответ 3

В качестве стандартных состояний С++ 11

§ 5.3.7

Оператор noexcept определяет, является ли оценка его операнда, который является неопубликованным операндом(Пункт 5), может генерировать исключение (15.1).

noexcept-expression:
noexcept ( expression )

Результатом оператора noexcept является константа типа bool и является rvalue. Результат оператора noexcept является ложным, если в потенциально-оцененном контексте выражение содержит

- потенциально оцениваемый вызов функции, функции-члена, указателя функции или функции-члена указатель, который не имеет спецификацию исключений без бросания (15.4), если только вызов не является константой выражение (5.19),

- потенциально оцениваемое выражение throw (15.1),

- потенциально оцениваемое выражение dynamic_cast dynamic_cast (v), где T является ссылочным типом, для которого требуется проверка времени выполнения (5.2.7) или

- потенциально оцениваемое выражение typeid (5.2.8), примененное к выражению glvalue, тип которого является тип полиморфного класса (10.3).

В противном случае результат будет истинным.

и

template <class T>
typename add_rvalue_reference<T>::type declval() noexcept; // as unevaluated operand

add_rvalue_reference имеет тип Transformation Trait и, явно не указан, но не требует создания объекта/функции.

Отсюда ясно, что struct foo; ... noexcept(std::declval<foo>()) является юридическим кодом. Где noexcept часть, кстати, эквивалентна noexcept(true), что эквивалентно просто noexcept, а noexcept(noexcept не имеет смысла, чтобы получить конструктор noexcept specifier, вы должны сделать это noexcept(foo()). Последнее также имеет силу, но, к сожалению, компиляторы не справляются с этим, вероятно, из-за порядка, как они строят блок для кода не С++ 11, и они еще не изменили эту модель. Это отражает характер ошибки, с которой вы сталкиваетесь в конкретной реализации libС++. По некоторым причинам add_rvalue_reference из-за наличия спецификатора noexcept пытается использовать объявление конструктора, и поскольку это происходит вне функции-члена, как упоминалось ранее, он терпит неудачу. Так что да, это ошибка библиотеки.

Ответ 4

Это правильный синтаксический способ, я могу сказать.

#include <utility>

struct foo
{
    int x{0};
    foo() noexcept {} // = default;
    void f() noexcept(noexcept(std::declval<foo&>())) {}
};

int main()
{ 
}