clang++ features
Недавно тут вышел clang-2.8, в котором таки пофиксили зарерпорчееный мной баг про -fno-rtti и try/catch.
Это должно было позволить собрать с помощью clang++ большой C++ проект на работе.
clang++ споймал пару прикольных подозрительных кусков кода (все примеры - реальные):
- Использование логических (|, &) операций вместо булевых (||, &&):
int CheckFileMask (const char * lpPath)
{
int result = CheckPathName (lpPath);
// Что-то делается ...
return result || 0x00000002;
}
...
bool SetDOSMask (char * lpMask)
{
// Что-то делается ...
int result = CheckFileMask (lpMask);
if( result && 0x00000001 != 0 )
{
// обрабатываем бит '0'
}
if( result && 0x00000002 != 0 )
{
// обрабатываем бит '1'
}
return result >= 0;
}
Clang говорит:
use of logical || with constant operand; switch to bitwise | or remove constant
[-Wconstant-logical-operand]
return result || 0x00000002;
исправляем:
int CheckFileMask (const char * lpPath)
{
int result = CheckPathName (lpPath);
// Что-то делается ...
return result | 0x00000002;
}
...
bool SetDOSMask (char * lpMask)
{
// Что-то делается ...
int result = CheckFileMask (lpMask);
if (result & 0x00000001)
{
// обрабатываем бит '0'
}
if (result & 0x00000002)
{
// обрабатываем бит '1'
}
return result >= 0;
}
- Неиспользуемый результат в выражении:
Оригинал:
while( ( * lpTmp == ' ' ) || ( * lpTmp == '\t' ) ) * lpTmp ++;
Предупреджение:
boot/emucfg.cpp:244:56: warning: expression result unused [-Wunused-value]
while( ( * lpTmp == ' ' ) || ( * lpTmp == '\t' ) ) * lpTmp ++;
^ ~~~~~~~~
Исправляем:
while( ( * lpTmp == ' ' ) || ( * lpTmp == '\t' ) ) lpTmp ++;
Было еще несколько красивых ошибок типизации, про которые лень писать.
- И самое интересное: clang++ правильно реализует ADL (argument dependent lookup), в отличие от g++.
Рассмотрим пример из бага, который я завел и тут же огрёб:
template<typename T> struct my_T {
() {
my_T((const T *)0);
my_foo }
};
struct User {
(); // explicit c-tor
User<char> t;
my_T};
// we specialize my_foo for 'const char *'
static void my_foo (const char *) {}
::User() { }
User
int main() {
;
User ureturn 0;
}
Этот код g++ собирает, а clang++ - нет:
a.cc:4:9: error: use of undeclared identifier 'my_foo'
my_foo ((const T *)0);
^
a.cc:16:7: note: in instantiation of member function 'my_T<char>::my_T' requested here
User::User() { }
Внимательно читаем по ссылке. Читаем еще раз!
Тут мы видим, что:
- my_foo() в шаблоне явно зависит от параметра шаблона
- значит имя является зависимым
- то есть его поиск производится в точке инстанцирования (определении конструктора User::User())
- значит my_foo() должно находиться при инстанцировании
А вот и нет! 4. пункт является верным только для типов, находящихся в пространстве имён my_T или пространстве имён точки инстанцирования.
Но(!) встроенные типы(int, char, etc.) находятся в “нигде” (ни в каком из пространств имён) и, как следствие, не подлежат поиску в ADL при специализации (это текущая формулировака поиска Кёнига в стандарте). Вот если бы я вместо char объявил свой тип - всё было бы хорошо:
template<typename T> struct my_T {
() {
my_T((const T *)0);
my_foo }
};
struct myO {};
struct User {
(); // explicit c-tor
User<myO> t;
my_T};
static void my_foo (const myO *) {}
::User() { }
User
int main() {
;
User ureturn 0;
}
g++ с таким положением дел не согласен и считает это ошибкой в стандарте. Правда, его в этом никто не поддерживает уже N лет.
clang++ считает, что стандарту 10 лет, формулировка не менялась и по сему можно реализовать в точности с формулировкой:
you'll need to move the declaration up, or instantiate my_T with a non-builtin type (so that
argument-dependent lookup can find my_foo in the namespace of that type)
so builtin types are special?
not special, really; they have no "associated namespaces", i.e., there's nowhere for the compiler to look
at instantiation time
do coercions work in such case? Will 'static void my_foo(const void *) {}' for my user's types?
implicit conversions do work
static functions might not, though; I'd have to check the standard
the C++ DR in question is at http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#225 ,
and apparently hasn't been touched in 10 years. me, I'll stick with what the standard says :)
Для достижения портабельности придется декларацию my_foo() вытаскивать раньше шаблонного кода.
Спасибо dgregor на oftc/#llvm и Andrew Pinski в gcc bugzilla за разьяснения.