Это действительно не дает объяснение, но здесь - то, что Stroustrup должен сказать об этом в "Выпуске Трети Языка Программирования на C++":
10.4.6.2 членских Констант
также возможно инициализировать статического интегрального постоянного участника путем добавления константное выражение инициализатор к его объявлению элемента. Например:
class Curious { static const int c1 = 7; // ok, but remember definition static int c2 = 11; // error: not const const int c3 = 13; // error: not static static const int c4 = f(17); // error: in-class initializer not constant static const float c5 = 7.0; // error: in-class not integral // ... };
Однако инициализированный участник должен все еще быть (исключительно) определен где-нибудь, и инициализатор не может быть повторен:
const int Curious::c1; // necessary, but don't repeat initializer here
я считаю это ошибкой. При необходимости в символьной константе в рамках объявления класса используйте перечислитель (4.8, 14.4.6, 15.3). Например:
class X { enum { c1 = 7, c2 = 11, c3 = 13, c4 = 17 }; // ... };
Таким образом, никакое членское определение не необходимо в другом месте, и Вы не испытываете желание объявить переменные, числа с плавающей запятой, и т.д.
И в Приложении C (Технические особенности) в Разделе C.5 (Константные выражения), Stroustrup говорит следующее о "константных выражениях":
В местах, таких как границы массива (5.2), маркировки случая (6.3.2) и инициализаторы для перечислителей (4.8), C++ требует константное выражение . Константное выражение оценивает к интегралу или постоянному перечислению. Такое выражение состоит из литералов (4.3.1, 4.4.1, 4.5.1), перечислители (4.8), и consts, инициализированный константными выражениями. В шаблоне целочисленный шаблонный параметр может также использоваться (C.13.3). Плавающие литералы (4.5.1) могут использоваться только если явно преобразованный в целочисленный тип. Функции, объекты класса, указатели и ссылки могут использоваться в качестве операндов к оператор (6.2) sizeof только.
Интуитивно, константные выражения являются простыми выражениями, которые могут быть оценены компилятором, прежде чем программа будет связана (9.1) и начнет работать.
Примечание, которое он в значительной степени не учитывает плавающую точку как способность играть в 'константных выражениях'. Я подозреваю, что плавающая точка была упущена из этих типов константных выражений просто, потому что они не достаточно 'просты'.
Что насчет чего-то вроде:
public static class EnumUtils
{
public static Nullable<T> Parse<T>(string input) where T : struct
{
//since we cant do a generic type constraint
if (!typeof(T).IsEnum)
{
throw new ArgumentException("Generic Type 'T' must be an Enum");
}
if (!string.IsNullOrEmpty(input))
{
if (Enum.GetNames(typeof(T)).Any(
e => e.Trim().ToUpperInvariant() == input.Trim().ToUpperInvariant()))
{
return (T)Enum.Parse(typeof(T), input, true);
}
}
return null;
}
}
Используется как:
MyEnum? value = EnumUtils.Parse<MyEnum>("foo");
(Примечание: использовалась старая версия try / catch
около Enum.Parse
)
private enum MyEnum
{
Enum1 = 1, Enum2 = 2, Enum3 = 3, Enum4 = 4, Enum5 = 5, Enum6 = 6,
Enum7 = 7, Enum8 = 8, Enum9 = 9, Enum10 = 10
}
private static Object ParseEnum<T>(string s)
{
try
{
var o = Enum.Parse(typeof (T), s);
return (T)o;
}
catch(ArgumentException)
{
return null;
}
}
static void Main(string[] args)
{
Console.WriteLine(ParseEnum<MyEnum>("Enum11"));
Console.WriteLine(ParseEnum<MyEnum>("Enum1"));
Console.WriteLine(ParseEnum<MyEnum>("Enum6").GetType());
Console.WriteLine(ParseEnum<MyEnum>("Enum10"));
}
ВЫХОД:
//This line is empty as Enum11 is not there and function returns a null
Enum1
TestApp.Program+MyEnum
Enum10
Press any key to continue . . .
У меня есть метод TryParseName
в UnconstrainedMelody , библиотека для служебных методов делегирования и перечисления, которая использует «невыразимые» ограничения с помощью некоторых уловок после сборки. (Код , использующий , библиотека не требует постбилда, просто для ясности.)
Вы бы использовали его так:
Foo foo;
bool parsed = Enums.TryParseName<Foo>(name, out foo);
В настоящее время у меня нет версии без учета регистра, но Я могу легко представить одного, если хотите. Обратите внимание, что этот не пытается анализировать числа, например, «12», как это делает встроенная версия, и не пытается анализировать списки флагов, разделенных запятыми. Я могу добавить версию flags позже, но я не вижу особого смысла в числовой версии.
Это делается без упаковки и без проверки типа времени выполнения. Наличие ограничения действительно удобно:
Я только что объединил синтаксис из здесь с обработкой исключений из здесь , чтобы создать следующее:
public static class Enum<T>
{
public static T Parse(string value)
{
//Null check
if(value == null) throw new ArgumentNullException("value");
//Empty string check
value = value.Trim();
if(value.Length == 0) throw new ArgumentException("Must specify valid information for parsing in the string", "value");
//Not enum check
Type t = typeof(T);
if(!t.IsEnum) throw new ArgumentException("Type provided must be an Enum", "TEnum");
return (T)Enum.Parse(typeof(T), value);
}
}
Вы можете вертеться немного, чтобы вернуть ноль вместо выдачи исключений.
Если вы используете .NET 3.5 (или даже 2.0, если вы удалите метод расширения), мне очень повезло с методами, описанными в этой статье:
Перечисления и Строки - Остановите безумие!
РЕДАКТИРОВАТЬ: Домен исчез и теперь представляет собой ферму ссылок. Я вытащил код (слегка измененный и добавленный со временем) из нашей кодовой базы на работе, который теперь вы можете найти здесь: