К моему удивлению, первое объявление вызывает ошибку компилятора. Возвращаемый тип функции Constexpr не является литеральным, но второе работает просто отлично.
Почему это происходит? Связано ли это с тем, что тип возвращаемой функции - шаблон?
Да. В первом случае компилятор может доказать, что эта конкретная специализация std::function
не является литеральным типом.
Во втором случае он должен знать T
, прежде чем узнает, является ли конкретная специализация std::function
литеральным типом.
template<class T>
struct example {
virtual T get() const {return {};}
~example() {}
};
template<>
struct example<int> {};
#if 0
template<class F>
constexpr example<double> test( F ) { return {}; }
#endif
template<class T, class F>
constexpr example<T> test2( F ) { return {}; }
Живой пример
test
незаконно; test2
в порядке. На самом деле, test2<int>( 3.14 )
является законным constexpr
вызовом.
Теперь у std::function
нет такой специализации , но компилятор не обязан доказывать это; в общем случае это требует решения проблемы остановки, что стандарт C ++ старается избегать от компиляторов.
Как уже упоминалось @MaxLanghof, такого рода ошибки приводят к "неправильной, не требующей диагностики" программе. Компилятор не обязан обнаруживать, что ваша constexpr
функция не может быть constexpr
, но если вы сделаете это, компилятор может делать все что угодно, в том числе генерировать сообщение об ошибке (или что-то еще хуже).
Таким образом, GCC не ошибается, чтобы пропустить ошибку, и clang не ошибается, чтобы выдать ошибку. Излучение ошибок Кланга в некотором смысле является более высоким QoI, чем пропуск GCC.