Тест на существование частного члена с использованием метапрограммирования, GCC vs clang, что правильно?

Это скорее вопрос стандартов C ++. Рассмотрим следующий код:

template <typename T>
class has_Data
{
   typedef char one;
   typedef long two;

   template <typename C> static one test( typeof(&C::Data) ) ;
   template <typename C> static two test(...);

public:
   enum { value = sizeof(test<T>(0)) == sizeof(char) };
};

class MyClass {
private:
   struct Data {
   };
};


void function(bool val = has_Data<MyClass>::value) {}

Приведенный выше код работает с gcc (GCC) 4.4.3

Однако с clang version 3.3 (2545b1d99942080bac4a74cda92c620123d0d6e9) (2ff97832e593926ea8dbdd5fc5bcf367475638a9)

это дает эту ошибку:

test_private_data.cpp:7:54: error: 'Data' is a private member of 'MyClass'
   template <typename C> static one test( typeof(&C::Data) ) ;
                                                     ^
/devshared/home/rhanda/test_private_data.cpp:7:37: note: while substituting explicitly-specified template arguments into function template 'test'
   template <typename C> static one test( typeof(&C::Data) ) ;
                                    ^
/devshared/home/rhanda/test_private_data.cpp:21:26: note: in instantiation of template class 'has_Data<MyClass>' requested here
void function(bool val = has_Data<MyClass>::value) {}
                         ^
1 error generated.

Какой из них правильный?

Из стандартного документа (n3485), Я нашел утверждение, которое, похоже, больше согласуется с clang, чем с gcc.

Контроль доступа применяется единообразно ко всем именам, независимо от того, упоминаются ли имена из объявлений или выражений.


person RamneekHanda    schedule 25.04.2013    source источник
comment
Вы имели в виду template <typename C> static one test( typename C::Data * );? Мне просто любопытно, что decltype( &C::Data ) делает для вас ...   -  person lapk    schedule 25.04.2013
comment
Это то же самое ... просто определение того, может ли функция быть решена в первую или нет ... Если я сделаю изменение, чтобы использовать ваш код ... он сделает то же самое, я полагаю.   -  person RamneekHanda    schedule 25.04.2013
comment
@RamneekHanda Разве decltype( &C::Data ) не type of pointer to a data-member C::Data? Потому что, если вы запустите свой тест, как показано, параметр function() val будет 0. Разве вы не имеете в виду, что это будет 1 для MyClass?   -  person lapk    schedule 25.04.2013
comment
Обратите внимание, что typeof не является частью стандарта C ++, это расширение GCC.   -  person NonNumeric    schedule 25.04.2013
comment


Ответы (1)


Я предполагаю, что GCC прав.

Прежде всего следует отметить, что никакой код, отличный от friend, не должен иметь возможность положительно сообщать о существовании данного закрытого члена. Так что, если вы пытаетесь это сделать, вам нужно изменить свой дизайн. Класс может делать что угодно со своими закрытыми членами, и другой код (кроме друзей) не должен знать об этом. Это задумано.

Однако существует принцип SFINAE: отказ замены не является ошибкой. Поскольку MyClass::Data является частным, код в has_Data должен, по моему мнению, действовать так, как если бы C::Data участника вообще не было. Следовательно, первая функция приведет к сбою подстановки, которая незаметно игнорируется, а используется вторая функция. Добавив немного больше кода, мой GCC 4.7.2 компилирует его без проблем, и has_Data<MyClass>::value оценивается как false. Правильный на мой взгляд СФИНАЕ.

Попытка подкрепить это мнение цитатой из документ, на который вы ссылались, я обнаружил следующее в пункте 8 раздела 14.8.2:

Примечание. Проверка доступа выполняется как часть процесса замены.

Это ненормативное примечание в стандарте, но мне кажется очень удобочитаемым и ясным указанием на то, что SFINAE действительно должен применяться в этой ситуации, точно так же, как GCC обрабатывает это.

Изменить: как указано в @hvd в комментарий, приведенный выше верно только для C ++ 11. В старых версиях стандарта ситуация была иной. Подробная информация об этом изменении содержится в проблеме 1170: проверка доступа во время вывода аргументов шаблона.

GCC не будет компилировать этот код с -std=c++03 или -std=c++11 из-за того, что typeof является расширением GNU. Тот факт, что -std=gnu++03 по-прежнему компилирует код, возможно, можно было бы считать неуместным, но, поскольку дальнейший путь основан на семантике C ++ 11, я бы не стал подавать отчет об этом.

person MvG    schedule 25.04.2013
comment
IIRC, это одна из новых функций C ++ 11, и до C ++ 11 проверка доступа должна была происходить позже. Вопрос не в том, скомпилирован ли код в режиме C ++ 11. - person ; 25.04.2013
comment
@hvd: Хороший момент! На основании этого ответа я узнал о Проблема 1170: Проверка доступа во время вывода аргументов шаблона, в которой это изменение обсуждалось и принималось решение. - person MvG; 25.04.2013