Russian Qt Forum

Qt => Общие вопросы => Тема начата: zavulon от Сентябрь 23, 2008, 15:30



Название: Ошибка компоновщика при использовании qMin(), qMax(), qBound()
Отправлено: zavulon от Сентябрь 23, 2008, 15:30
Попытка использовать статические константы-члены в функциях qMin(), qMax(), qBound() выдает следющую ошибку компоновщика:
   undefined reference to `<статическая константа>'.

Вот пример кода:
Код:
class Test
{
public:
static const float KValue=3.0;
Test();
};

Test::Test()
{
// Может быть в любой функции, необязательно в конструкторе
float foo = 1.0;
foo = qMax( foo, KValue );
}

int main()
{
// где-нибудь в программе
Test obj;
}

С другой стороны, при использовании обычных локальных констант в qMax (и подобных) всё проходит отлично.
Пример нормальной работы:
Код:
float foo = 1.0;
const float value = KValue; // Скопировать статическую константу в локальную
foo = qMax( foo, value );

Напишите, если кто знает из-за чего это происходит.

Используемая платформа:
Kubuntu 8.04
Qt 4.4.0 and 4.4.1
gcc 4.2.4


Название: Re: Ошибка компоновщика при использовании qMin(), qMax(), qBound()
Отправлено: pastor от Сентябрь 23, 2008, 15:55
qMin(), qMax(), qBound() здесь непричем. Вы пытаетесь инициализировать значением статический константный неинтегральный тип, а так делать нельзя. Только статические константные интегральные типы могут инициализироваться значением внутри класса.

Т.е. нужно писать так:

Код:
class Test
{
public:
static const float KValue;
Test();
};

const float Test::KValue = 3.0;


Название: Re: Ошибка компоновщика при использовании qMin(), qMax(), qBound()
Отправлено: zavulon от Сентябрь 23, 2008, 17:25
Огромное спасибо!
Действительно, в C++ простые типы данных делятся на две группы: целые (интегральные, integral) и нецелые. Целые типы имеют конечный и заранее известный набор значений: int, char, short, long и перечисления (enumerations). Все остальные, например float, double, char[], - это нецелые типы.

Вообще, в C++ в заголовке класса можно только объявлять статические константы. Опеределять их значение нужно уже вне объявления класса - как правило это .cpp - файл. Единственное исключение делается для целых (интегральных) типов - такие статические константы можно инициализировать сразу же при объявлении внутри класса. http://www.informit.com/blogs/blog.aspx?uk=30-C-Tips-in-30-Days-Tip-26-in-class-initialization-of-const-static-data-members-of-integral-types (http://www.informit.com/blogs/blog.aspx?uk=30-C-Tips-in-30-Days-Tip-26-in-class-initialization-of-const-static-data-members-of-integral-types).

Поэтому, в данном случае с типом float не было так гладко. Но остается загадкой, почему мой второй вариант кода тогда работал? Т.е. когда static const float инициализируется внутри класса, а потом в каком-нибудь методе значение такой константы можно присвоить локальной переменной.


Название: Re: Ошибка компоновщика при использовании qMin(), qMax(), qBound()
Отправлено: zavulon от Сентябрь 23, 2008, 19:41
Становится все более интересно. Если float заменить на int, ничего не меняется. Компоновщик выдает ту же ошибку уже для целого типа.
Функции qMax() и др. реализованы как шаблоны, а аргументы передаются с помощью ссылок:
Код:
template<class T>
const T& qMax( const T& value1, const T& value2 );
Не в ссылках ли кроется загадка? На этапе компиляции все нормально. Ошибка происходит при компоновке. Т.е. функции qMax() явно не нравится, что ей передают адрес статической константы.
Если эту же константу передать в любую другую Qt-функцию, которая берет в качестве аргумента именно int или float (т.е. по значению), то все прекрасно работает.

Пример рабочего кода:
Код:
#include <QList>

class Test
{
public:
static const int KPos = 5;
Test();
QList< int > list;
};

Test::Test()
{
// Может быть в любой функции, необязательно в конструкторе
list.insert( KPos, 10 );   // Тут ок

/* А вот в этой строке будет ошибка:
int res = qMax(3, KPos);*/
}

int main()
{
// где-нибудь в программе
Test obj;
}


Название: Re: Ошибка компоновщика при использовании qMin(), qMax(), qBound()
Отправлено: pastor от Сентябрь 23, 2008, 20:15
Приведите полное сообщение об ошибке

//upd

Проверил код на MSVC2005, все работает нормально. Позже проверю под Линуксом


Название: Re: Ошибка компоновщика при использовании qMin(), qMax(), qBound()
Отправлено: Alex03 от Сентябрь 24, 2008, 07:36
А у меня вот тут вопрос возник...
А чем отличаются в данном случае статическая и не статическая константы?
В случае:
Код:
class Test
{
...
    const float f = 5.0;
...
};
компилятор не будет в каждый объект засовывать константу... или будет?
Да и вообще есть ли точные правила когда константа обязательно представленна ячейкой памяти (и соответственно имеет свой адрес и т.д.), а когда может быть полностью соптипизированна (на той же x86 платформе тот же int проще константой в коде иметь, чем вытаскивать по адресу, порой ещё и не прямому).

Всё... пора идти за страуструпом (книга между домом и работой уже наверное месяц катается без дела).


Название: Re: Ошибка компоновщика при использовании qMin(), qMax(), qBound()
Отправлено: zavulon от Сентябрь 25, 2008, 16:29
Я пришел к выводу, что ошибка вызвана оптимизацией компилятора. Компилятор все константы, если это возможно, заменяет на числовые значения, которые естественно не имеют своего адреса. Попытка взять адрес такой константы в функции qMax(), приводит к ошибке. qMax(), qMin() и qBound() принимают аргументы по ссылке, а не по значению.