"Инициализатор элемента по умолчанию, необходимый для определения класса включения вне функций-членов" - мой код плохо сформирован?
#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
сделает компиляцию кода.
Ответы
Ответ 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()
{
}
Ответ 5
Похоже, это может быть связано с этим фиксатором в ноябре. https://lists.llvm.org/pipermail/cfe-commits/Week-of-Mon-20161121/177858.html
Это ошибка компилятора. Сообщалось ли?