Это потому что это в определении класса.Вы не можете использовать статические функции класса во время компиляции до полного определения класса.
Если причина неясна, ваш код на самом деле такой:
class cColor {
public:
enum eValue { k_Red, k_Green, k_Blue };
static constexpr std::size_t NumValues() { return 3; }
static constexpr std::array<cColor::eValue, cColor::NumValues()> Values() { return {k_Red, k_Green, k_Blue}; }
};
Видите ли, когда вы говорите std::array<cColor::eValue, cColor::NumValues()>
в качестве возвращаемого типа, вы используете cColor::NumValues()
, который использует cColor
, который еще не был определен (потому что он находится внутри определения класса.
Выэффективное определение компонента cColor
в терминах самого себя.
Проблема решается перемещением компонента со ссылками на себя вне класса (одного или обоих):
#include <iostream>
#include <array>
static constexpr std::size_t NumValues() { return 3; }
class cColor {
public:
enum eValue { k_Red, k_Green, k_Blue };
static constexpr std::array<eValue, NumValues()> Values() { return {k_Red, k_Green, k_Blue}; }
};
int main() {
std::cout << "NumColors=" << NumValues() << '\n';
}
Редактировать:
Чтобы ответить на ваш вопрос о том, почему именно использование функции constexpr вызывает проблемы (в отличие от вашего пересмотренного вопроса с использованием переменной constexpr), я дамследующая информация:
Вы когда-нибудь замечали, что не можете использовать функцию до ее объявления, но вы можете использовать функцию-член до ее объявления (в определении класса, которое я имею в виду).
Это потому, что компилятор C ++ затуманивает весь класс, включая переменные-члены / статические методы и методы / статические методы, прежде чем он сделает что-либо еще.Поэтому методы / статические методы (которые я в совокупности будем называть функциями-членами) должны иметь правильно сформированные объявления до того, как будет определена какая-либо их фактическая реализация - это, конечно, включает их возвращаемые типы.
Таким образом,во время компиляции, когда проверяется объявление для Values()
, он знает, что тип возвращаемого значения зависит от NumValues()
, и знает, что NumValues()
возвращает std::size_t
, но он еще не исследовал реализацию каких-либо функций-членов вкласс еще.Таким образом, он еще не знает (пока), что NumValues()
вернет 3
.
Таким образом, вы также можете решить эту проблему с помощью отложенного вывода типа возврата.Реальная суть проблемы заключается в том, что Values()
должен иметь правильно сформированный тип возврата, прежде чем будет проверена реализация функций-членов его класса.
Вот еще одно решение, которое может осветить специфику проблемы:
#include <iostream>
#include <array>
class cColor {
public:
enum eValue { k_Red, k_Green, k_Blue };
static constexpr std::size_t NumValues() { return 3; }
static constexpr auto Values() { return std::array<eValue, NumValues()>{k_Red, k_Green, k_Blue}; }
};
int main() {
std::cout << "NumColors=" << cColor::NumValues() << '\n';
}
Видите ли, auto
является допустимым типом возвращаемого значения для подписи, а фактический тип возвращаемого значения выводится из реализации метода, которая в этот момент знает реализацию NumValues()
.
Причина этого странного порядка разбора компилятора заключается в том, что вам не нужно размещать методы в определенном порядке, чтобы они компилировали (при нормальных обстоятельствах - читайте дальше) .Таким образом, все методы известны до каких-либо реализаций, что является своего рода , как если бы у вас было предварительное объявление для каждого метода в классе.
И просто, если вам интересно, да, перемещениеопределение / объявление NumValues()
после Values()
вызовет ошибку компиляции, потому что наш трюк больше не работает, так как реализация для NumValues()
проверяется после реализации для Values()
и, следовательно, Values()
не работаетзнать NumValues()
возвращает 3
:
class cColor {
public:
enum eValue { k_Red, k_Green, k_Blue };
// THIS ORDERING FAILS TO COMPILE
static constexpr auto Values() { return std::array<eValue, NumValues()>{k_Red, k_Green, k_Blue}; }
static constexpr std::size_t NumValues() { return 3; }
};
Ваш пример работает, потому что переменные constexpr должны быть определены и объявлены одновременно, поэтому значение 3
известно с этого момента и, следовательно,декларации действительны.Однако, если вы переместите объявление / определение статической переменной-члена constexpr после Values()
, у вас снова будет ошибка компиляции, которую можно исправить с помощью хака auto
, который я продемонстрировал выше.
class cColor {
public:
enum eValue { k_Red, k_Green, k_Blue };
// THIS ORDERING FAILS TO COMPILE
static constexpr std::array<eValue, NumValues> Values() { return {k_Red, k_Green, k_Blue}; }
static constexpr std::size_t NumValues = 3;
};
class cColor {
public:
enum eValue { k_Red, k_Green, k_Blue };
// AUTO TRICK MAKES THIS WORK
static constexpr auto Values() { return std::array<eValue, NumValues>{k_Red, k_Green, k_Blue}; }
static constexpr std::size_t NumValues = 3;
};