2015-01-07 3 views
9

Этот вопрос касается взаимосвязи между шаблонами и статическими интегральными константами в Visual Studio C++ 2013 с флагом/Za. Это имеет значение для библиотеки ускорения.Статические константы целочисленного типа C++: вне определения класса


Во-первых, давайте проверим код без шаблонов:

struct easy 
{ 
    static const int a = 5; 
    const int b; 

    easy(int b_) : b(std::max(b_, a)) 
    {} 
}; 

const int easy::a; 

int main() 
{ 
    easy d_Easy(0); 
    return 0; 
} 

Согласно странице руководства для compiler option /Za: «В соответствии со стандартом (/ Za), вы должны сделать вне класса определение членов данных ". Пример на этой странице и вышеприведенный код объявляют статическую константу внутри класса и определяют ее значение там. Необходимость определения класса не объясняется в this link.


Теперь, давайте посмотрим на проблему с шаблонами.

template< class T > 
struct problem 
{ 
    static const int a = 5; 
    const int b; 

    problem(int b_) : b(std::max(b_, a)) 
    {} 
}; 

template< class T > 
const int problem<T>::a; 

int main() 
{ 
    problem<char> d_Bad(666); 
    return 0; 
} 

При компиляции с/Za, компоновщик выдает сообщение об ошибке "LNK2019: неразрешенный внешний символ". Эта ошибка не появляется с опцией /Ze. Основная проблема заключается в том, что некоторые дополнительные библиотеки используют BOOST_STATIC_CONSTANT и BOOST_NO_INCLASS_MEMBER_INITIALIZATION в коде, подобном описанному выше snipet.


Взлом некоторые из них:

template< class T > 
struct fixed 
{ 
    static const int a; 
    const int b; 

    fixed(int b_) : b(std::max(b_, a)) 
    {} 
}; 

template< class T > 
const int fixed<T>::a = 5; 

int main() 
{ 
    fixed<char> d_Good(777); 
    return 0; 
} 

Этот код в настоящее время составляет с/Za.

Вопросы:

1) Что стандарт C++ 11 говорят о шаблонах и статических интегральных констант? Могут ли/они должны иметь определение класса, но их значение должно быть указано в определении класса?

2) Есть ли у подголовника обходные пути?


UPDATE

Важно держать std::max в коде, потому что (я думаю), он пытается получить ссылку на его параметры. Если вы используете b_<a, тогда компилятор просто оптимизирует эти константы.

+0

_ «Если вы используете b_ sehe

+0

Возможно, последний параграф моего ответа необходим для вашего реального вопроса?Если это так, очевидно, вам ** всегда ** необходимо отделить статическую инициализацию элемента в отдельную единицу перевода. – sehe

+0

@sehe: это актуально, потому что я не могу скомпилировать boost :: pool с/Za в Visual Studio C++ 13. Я проследил виновника в поведении в вопросе. После того, как я узнаю, кто несет ответственность, я сообщаю об ошибке либо людям с повышенным интересом, либо репатриантам MS. – Hector

ответ

5

Прежде всего, объявление статического элемента данных в классе никогда не является определением. Если вы используете эту переменную odr, определение должно быть представлено - конечно, вне класса.

std::max действительно использует odr-использование a, так как его параметры являются ссылками, а переменные являются odr-используемыми, если ссылка привязана к ним ([basic.def.odr]/3). (Это действительно проблема с max - это не должно быть odr-use a, действительно.)
В ответе @ sehe он использует тернарный оператор напрямую, избегая использования odr, поскольку преобразование lvalue-to-rvalue немедленно применяется и дает постоянное выражение.

  1. Это довольно просто. Когда требуется определение статического элемента данных шаблона класса, то есть когда этот член используется как odr, как в вашем случае, создается (определение области пространства имен). [Temp.inst]/2:

    Если член шаблона класса или шаблона члена не было явно инстанциированы явно специализированы, специализация элемента неявно инстанцирован, когда специализация Реферировано в контексте, который требует определения члена; , в частности, инициализация (и любые связанные с ней побочные эффекты) статического элемента данных не возникает, если статический элемент данных сам используется таким образом, чтобы потребовалось определение статического члена данных .

    И определение выполняется точно так же, как вы это сделали. [Temp.static]/1:

    Определение для статического элемента данных или шаблона члена статических данные могут быть предоставлено в области видимости пространства имен, охватывающее определение шаблона класса статического члена.

    [Пример:

    template<class T> class X { 
        static T s; 
    }; 
    template<class T> T X<T>::s = 0; 
    

    Инициализатором могут быть поставлены в декларации в классе, когда элемент имеет const интегрального типа, но это не влияет на семантику ODR в это уважение. Определение по-прежнему требуется таким же образом и написано так же, как вы это делали.

Следовательно, кажется, что вы видите только ошибку VC++.

+0

* «std :: max действительно использует odr-a, поскольку его параметры являются ссылками» * Вы уверены, что этого достаточно, чтобы требовать определения? http://coliru.stacked-crooked.com/a/1d0913fc7bc14b31 – dyp

+0

А, кажется, * набор потенциальных результатов * любого выражения вызова функции пуст; поэтому используется ODR. Правильно? (Функция 'max', которая принимает значение, не потребует этого, так как существует подвыражение, которое применяет преобразование ltr в' ex'.) – dyp

+0

I [сообщено] (https://connect.microsoft.com/VisualStudio/ обратная связь/детали/1079894) это как ошибка. – Hector

2

Обходной, который я использовал в течение длительного времени, и в последнее время стал более полезным в C++ 11:

Live On Coliru

struct easy 
{ 
    enum : int { a = 5 }; 
    int b; 

    constexpr easy(int b_) : b(b_<a? a : b_) 
    {} 
}; 

Он стал более полезным, потому что теперь вы можете указать базовый тип:

struct Container 
{ 
    enum special_position : size_t { npos = size_t(-1), at_bof = 0 }; 
}; 

Конечно, он ограничен (пользовательские/примитивные) интегральные типы.


Внешне определенные константы могут иметь преимущество, что они могут фактически быть изменены только перекомпиляции единицы перевода, который определяет значение.

+0

Вам не нужен 'enum class', если вы хотите указать базовый тип? – vsoftco

+1

@vsoftco no (по крайней мере, не на моих компиляторах) – sehe

+0

@vsoftco: «Класс» делает его строго типизированным. Без него декларации выходят в структуру контейнера. – Hector

Смежные вопросы