Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Private member existence test using meta programming, GCC vs clang, which is right?

Tags:

c++

gcc

clang

This is more of a c++ standards question. Consider the following code:

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) {}

The above code works with gcc (GCC) 4.4.3

However with clang version 3.3 (2545b1d99942080bac4a74cda92c620123d0d6e9) (2ff97832e593926ea8dbdd5fc5bcf367475638a9)

it gives this error:

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.

Which one is right?

From standard document (n3485), I found a statement which seems to agree with clang more than gcc.

Access control is applied uniformly to all names, whether the names are referred to from declarations or expressions.

like image 619
RamneekHanda Avatar asked Oct 21 '22 11:10

RamneekHanda


1 Answers

I would assume that GCC is right.

The first thing to note is that no non-friend code should be able to positively report the existence of a given private member. So if that is what you try to do, you have to modify your design. A class can do anything with its private members, and other code (excepting friends) should have no way to know about it. That's by design.

However, there is the SFINAE principle: substitution failure is not an error. Since MyClass::Data is private, the code in has_Data should – in my opinion – act as if there was no C::Data member at all. Hence the first function would lead to a substitution failure, which gets silently ignored, and the second function is the one used. Adding a bit more code, my GCC 4.7.2 compiles this without issues and with has_Data<MyClass>::value evaluating to false. Correct SFINAE in my opinion.

Trying to back this opinion up with a quotation from the document you referred to, I found the following in section 14.8.2 paragraph 8:

Note: Access checking is done as part of the substitution process.

This is a non-normative note in the standard, but to me appears to be a very readable and clear indication that SFINAE should in fact apply in this situation, just the way GCC handles it.

Edit: As @hvd pointed out in a comment, the above is only true for C++11. In older versions of the standard, the situation used to be different. Issue 1170: Access checking during template argument deduction has details on that change.

GCC will not compile this code with -std=c++03 or -std=c++11 due to the fact that typeof is a GNU extension. The fact that -std=gnu++03 still compiles the code might perhaps be considered inappropriate, but since the way forward is using the C++11 semantics, I wouldn't bother filing a report about this.

like image 144
MvG Avatar answered Oct 27 '22 22:10

MvG