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
за разьяснения.