5

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.

MvG
  • 57,380
  • 22
  • 148
  • 276
RamneekHanda
  • 171
  • 5
  • Did you mean `template static one test( typename C::Data * );`? I'm just curious what `decltype( &C::Data )` does for you... – lapk Apr 25 '13 at 05:15
  • Its the same.. just detecting whether the function can be resolved into the first one or not.. If i make a change to use your code.. it does the same thing, i suppose. – RamneekHanda Apr 25 '13 at 05:23
  • @RamneekHanda Isn't `decltype( &C::Data )` a `type of pointer to a data-member C::Data`? Because, if you do run your test as shown, the `function()` parameter `val` is `0`. Don't you mean it to be `1` for `MyClass`? – lapk Apr 25 '13 at 05:27
  • Note that `typeof` is not part of the C++ standard, it's a GCC extension. – NonNumeric Apr 25 '13 at 09:01
  • See also [Can SFINAE detect private access violations?](http://stackoverflow.com/q/8984013/1468366). – MvG Apr 25 '13 at 10:28

1 Answers1

3

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.

Community
  • 1
  • 1
MvG
  • 57,380
  • 22
  • 148
  • 276
  • 1
    IIRC, that is one of the new features of C++11, and before C++11, access checking was supposed to happen later. The question doesn't state whether the code is compiled in C++11 mode. –  Apr 25 '13 at 09:35
  • @hvd: Good point! Based on [this answer](http://stackoverflow.com/a/8984268/1468366) I found out about [Issue 1170: Access checking during template argument deduction](http://wg21.cmeerw.net/cwg/issue1170) where this change was discussed and decided. – MvG Apr 25 '13 at 10:31