Я сегодня столкнулся с интересной проблемой. Рассмотрим этот простой пример:
template <typename T>
void foo(const T & a) { /* code */ }
// This would also fail
// void foo(const int & a) { /* code */ }
class Bar
{
public:
static const int kConst = 1;
void func()
{
foo(kConst); // This is the important line
}
};
int main()
{
Bar b;
b.func();
}
При компиляции выдает ошибку:
Undefined reference to 'Bar::kConst'
Теперь я почти уверен, что это потому, что static const int
нигде не определено, что является преднамеренным, потому что, согласно моему пониманию, компилятор должен иметь возможность производить замену во время компиляции и не нуждаться в определении. Однако, поскольку функция принимает const int &
параметр, кажется, что она не выполняет подстановку, а вместо этого предпочитает ссылку. Я могу решить эту проблему, внеся следующие изменения:
foo(static_cast<int>(kConst));
Я считаю, что теперь это заставляет компилятор создать временный int, а затем передать ссылку на него, что он может успешно сделать во время компиляции.
Мне было интересно, было ли это намеренным, или я слишком многого жду от gcc, чтобы справиться с этим случаем? Или мне почему-то не следует этого делать?
const int kConst = 1;
того же результата. Кроме того, редко есть причина (я не могу придумать ни одной), чтобы функция принимала параметр типаconst int &
- просто используйтеint
здесь.static
выдает ошибку `ISO C ++ запрещает инициализацию члена 'kConst' ... делая 'kConst' статическим. 'std::min( some_val, kConst)
, посколькуstd::min<T>
имеет параметры типаT const &
, и подразумевается, что нам нужно передать ссылку на kConst. Я обнаружил, что это происходит только при отключенной оптимизации. Исправлено использование статического приведения.Ответы:
Это намеренно, 9.4.2 / 4 говорит:
Когда вы передаете статический член данных по константной ссылке, вы «используете» его, 3.2 / 2:
Фактически, вы "используете" его, когда также передаете его по значению или в
static_cast
. Просто GCC позволил вам сняться с крючка в одном случае, но не в другом.[Изменить: gcc применяет правила из черновиков C ++ 0x: «Переменная или неперегруженная функция, имя которой отображается как потенциально оцениваемое выражение, используется odr, если только это не объект, который удовлетворяет требованиям для появления в константе выражение (5.19) и преобразование lvalue-to-rvalue (4.1) применяется немедленно. ". Статическое приведение выполняет преобразование lvalue-rvalue немедленно, поэтому в C ++ 0x оно не «используется».]
Практическая проблема со ссылкой на const заключается в том, что
foo
он вправе взять адрес своего аргумента и сравнить его, например, с адресом аргумента из другого вызова, хранящегося в глобальном. Поскольку статический член данных является уникальным объектом, это означает, что если вы вызываетеfoo(kConst)
из двух разных TU, то адрес переданного объекта должен быть одинаковым в каждом случае. AFAIK GCC не может этого добиться, если объект не определен в одном (и только одном) TU.Хорошо, в данном случае
foo
это шаблон, следовательно, определение видно во всех TU, поэтому, возможно, компилятор теоретически мог бы исключить риск того, что он что-то сделает с адресом. Но в целом вам, конечно, не следует брать адреса или ссылки на несуществующие объекты ;-)источник
template <int N> int intvalue() { return N; }
. Тогда сintvalue<kConst>
,kConst
появляется только в контексте , требующий интегральное выражение постоянная, и поэтому не используются. Но функция возвращает временный объект с тем же значениемkConst
, что и, и который может связываться с константной ссылкой. Однако я не уверен, что может быть более простой способ перенести принудительное исполнение,kConst
которое не используется.r = s ? kConst1 : kConst2
) с gcc 4.7. Я решил это с помощью актуального файлаif
. В любом случае спасибо за ответ!Если вы пишете статическую переменную const с инициализатором внутри объявления класса, это как если бы вы написали
class Bar { enum { kConst = 1 }; }
и GCC будет относиться к нему так же, что означает, что у него нет адреса.
Правильный код должен быть
class Bar { static const int kConst; } const int Bar::kConst = 1;
источник
Это действительно верный случай. Тем более, что foo может быть функцией из STL, например std :: count, которая принимает в качестве третьего аргумента const T & .
Я потратил много времени, пытаясь понять, почему у компоновщика были проблемы с таким базовым кодом.
Сообщение об ошибке
сообщает нам, что компоновщик не может найти символ.
$nm -C main.o 0000000000000000 T main 0000000000000000 W void foo<int>(int const&) 0000000000000000 W Bar::func() 0000000000000000 U Bar::kConst
Из буквы "U" видно, что Bar :: kConst не определен. Следовательно, когда компоновщик пытается выполнить свою работу, он должен найти символ. Но вы только объявляете kConst и не определяете его.
Решение в C ++ также состоит в том, чтобы определить его следующим образом:
template <typename T> void foo(const T & a) { /* code */ } class Bar { public: static const int kConst = 1; void func() { foo(kConst); // This is the important line } }; const int Bar::kConst; // Definition <--FIX int main() { Bar b; b.func(); }
Затем вы можете увидеть, что компилятор поместит определение в сгенерированный объектный файл:
$nm -C main.o 0000000000000000 T main 0000000000000000 W void foo<int>(int const&) 0000000000000000 W Bar::func() 0000000000000000 R Bar::kConst
Теперь вы можете увидеть букву «R», означающую, что она определена в разделе данных.
источник
>nm -C main.o | grep kConst
дает мне только одну строку0000000000400644 R Bar::kConst
.nm -C lib.a
дает вамConstants.o: 0000000000000000 R Bar::kConst
иmain_file.o: U Bar::kConst ...
.g ++ версии 4.3.4 принимает этот код (см. эту ссылку ). Но g ++ версии 4.4.0 отвергает это.
источник
Я думаю, что этот артефакт C ++ означает, что всякий раз, когда на
Bar::kConst
него ссылаются, вместо него используется его буквальное значение.Это означает, что на практике нет переменной, на которую можно было бы ссылаться.
Возможно, вам придется сделать это:
void func() { int k = kConst; foo(k); }
источник
foo(static_cast<int>(kConst));
, верно?Вы также можете заменить его функцией-членом constexpr:
class Bar { static constexpr int kConst() { return 1; }; };
источник
Простой трюк: используйте
+
передkConst
переданной функцией. Это предотвратит взятие ссылки на константу, и таким образом код не будет генерировать запрос компоновщика к константному объекту, а вместо этого продолжит работу со значением константы времени компилятора.источник
static const
значения, которое инициализируется при объявлении. Это всегда приводило к ошибке компоновщика, и когда та же самая константа также объявлялась отдельно в объектном файле, это тоже было бы ошибкой. Компилятор также полностью осведомлен о ситуации.static_cast<decltype(kConst)>(kConst)
.foo()
исходный вопрос будет нуждаться в адресе, и нет механизма, чтобы рассматривать его как временную копию всего массива.У меня возникла та же проблема, о которой упоминал Cloderic (static const в тернарном операторе
r = s ? kConst1 : kConst2
:), но он пожаловался только после отключения оптимизации компилятора (-O0 вместо -Os). Произошло на gcc-none-eabi 4.8.5.источник