May pointer to members circumvent the access level of a member? - c++

Our infamous litb has an interesting article on how to circumvent the access check.
It is fully demonstrated by this simple code:
#include <iostream>
template<typename Tag, typename Tag::type M>
struct Rob {
friend typename Tag::type get(Tag) {
return M;
}
};
// use
struct A {
A(int a):a(a) { }
private:
int a;
};
// tag used to access A::a
struct A_f {
typedef int A::*type;
friend type get(A_f);
};
template struct Rob<A_f, &A::a>;
int main() {
A a(42);
std::cout << "proof: " << a.*get(A_f()) << std::endl;
}
Which compiles and runs (output 42) with gcc 4.3.4, gcc 4.5.1, gcc 4.7.0 (see user1131467's comment) and compiles with Clang 3.0 and Comeau C/C++ 4.3.10.1 in C++03 strict mode and MSVC 2005.
I was asked by Luchian on this answer in which I used it to justify that it was actually legal. I agree with Luchian that it is weird, however both Clang and Comeau are close contenders for the most "Standard" compilers available (much more so than MSVC by default)...
And I could not find anything in the drafts of the Standards I have available (n3337 being the last version I got my hands on).
So... can anyone actually justifies that it is legal or not ?

Yes, it's legal. The relevant text is at §14.7.2/12, talking about explicit template instantiation:
12 The usual access checking rules do not apply to names used to specify explicit instantiations. [ Note: In particular, the template arguments and names used in the function declarator (including parameter types, return types and exception specifications) may be private types or objects which would normally not be accessible and the template may be a member template or member function which would not normally be
accessible. — end note ]
Emhpasis mine.

The code is clearly illegal (and requires a compile time diagnostic).
In the line:
template struct Rob<A_f, &A::a>;
the expression A::a accesses a private member of A.
The standard is very clear about this: “Access control is applied
uniformly to all names, whether the names are referred to from
declarations or expressions.“ (§11/4, emphasis added). Since a is a private name in A, any reference to it outside of A is illegal.

Related

Using declaration to abbreviate type of a template in c++ [duplicate]

When I compile the following snippet with g++
template<class T>
class A
{};
template<class T>
class B
{
public:
typedef A<T> A;
};
the compiler tells me
error: declaration of ‘typedef class A<T> B<T>::A’
error: changes meaning of ‘A’ from ‘class A<T>’
On the other hand, if I change the typedef to
typedef ::A<T> A;
everything compiles fine with g++. Clang++ 3.1 doesn't care either way.
Why is this happening? And is the second behavior standard?
g++ is correct and conforming to the standard. From [3.3.7/1]:
A name N used in a class S shall refer to the same declaration in its
context and when re-evaluated in the completed scope of S. No
diagnostic is required for a violation of this rule.
Before the typedef, A referred to the ::A, however by using the typedef, you now make A refer to the typedef which is prohibited. However, since no diagnostic is required, clang is also standard conforming.
jogojapan's comment explains the reason for this rule.
Take the following change to your code:
template<class T>
class A
{};
template<class T>
class B
{
public:
A a; // <-- What "A" is this referring to?
typedef A<T> A;
};
Because of how class scope works, A a; becomes ambiguous.
I will add to Jesse's answer about the seemingly peculiar behavior of GCC in compiling:
typedef A<T> A;
vs
typedef ::A<T> A;
This also applies to using statements as well of the form:
using A = A<T>;
using A = ::A<T>;
What seems to be happening within GCC, is that during the compilation of the typedef/using statement declaring B::A, that the symbol B::A becomes a valid candidate within the using statement itself. I.e. when saying using A = A<T>; or typedef A<T> A; GCC considers both ::A and B::A valid candidates for A<T>.
This seems odd behavior because as your question implies, you don't expect the new alias A to become a valid candidate within the typedef itself, but as Jesse's answer also says, anything declared within a class becomes visible to everything else inside the class - and in this case apparently even the declaration itself. This type of behavior may be implemented this way to permit recursive type definitions.
The solution as you found is to specify for GCC precisely which A you're referring to within the typedef and then it no longer complains.

Why does GCC allow a deduced return type in this function template when MSVC and Clang don't?

Code sample:
class A
{
static constexpr auto GetInt() noexcept { return 6; }
template<int N>
std::enable_if_t< N >= GetInt(), int> func() { return N; }
};
https://godbolt.org/z/-0pwIQ
Clang and MSVC both claim that GetInt() can't be used because it's not defined at that point, however GCC compiles with no errors or warnings.
My best guess for why the error occurs is that because the class is incomplete at the point that func(), member functions are considered undefined, and because auto relies on the function definition to deduce the return type, the compiler can't use it to generate a function signature.
However, that doesn't explain why GCC is allowing it. Is it incorrect to do so?
As it is already mentioned in the comments, this is one of C++ Standard Core Language Active Issues: http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#2335
It is still under discussion.
Though it does look like Clang/MSVC behavior might become standard:
Notes from the June, 2018 meeting:
The consensus of CWG was to treat templates and classes the same by
"instantiating" delayed-parse regions when they are needed instead of at the
end of the class.

Why does VC++2013 refuse to compile nested types, made visibile with the using keyword, when used as a template functions return type?

Visual Studio 2013 (update 2) throws a compile-time error when compiling a template function who's return type is a nested type name, which has been hidden through multiple inheritance, and made visible again with the using keyword; as in the following code:
struct Base1
{
typedef int value_type;
};
struct Base2
{
typedef double value_type;
};
struct Derived : Base1, Base2
{
using Base1::value_type;
};
template<typename T>
typename T::value_type nullary_function() { return 0; }
template<typename T>
typename T::value_type unary_function(T t) { return 0; }
int main()
{
nullary_function<Derived>(); // Error: C2770
unary_function( Derived() ); // Error: C2893
return 0;
}
(The error numbers vary depending upon whether the function accepts template arguments or not as shown in the comments.)
G++ 4.7 accepts this code.
Specifically, I would like to know what the C++ standard has to say on the matter and whether or not this is a VC++ compiler bug. (It would appear to me that it is seeing as making nested types visible with the using keyword makes them visible in every other situation as far as I'm aware.)
I am also aware that the line with the using keyword may be changed from
using Base1::value_type;
to
typedef Base1::value_type value_type;
in order to get the code to compile and function correctly, but it seems bad for portability for some (potentially) valid code to compile on some compilers and not others - hence the desire for clarification.
This is indeed a compiler bug -- ICC, CLang, and G++ all accept this code as verified on Godbolt.
The most applicable language in the standard I could find is 7.3.3 (namespace.udecl) p2 (quoted from N3337)
Every using-declaration is a declaration and a member-declaration and so can be used
in a class definition.
P.S. ICC, CLang, and G++ all accept the typedef version as well.

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

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.
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.

typedef changes meaning

When I compile the following snippet with g++
template<class T>
class A
{};
template<class T>
class B
{
public:
typedef A<T> A;
};
the compiler tells me
error: declaration of ‘typedef class A<T> B<T>::A’
error: changes meaning of ‘A’ from ‘class A<T>’
On the other hand, if I change the typedef to
typedef ::A<T> A;
everything compiles fine with g++. Clang++ 3.1 doesn't care either way.
Why is this happening? And is the second behavior standard?
g++ is correct and conforming to the standard. From [3.3.7/1]:
A name N used in a class S shall refer to the same declaration in its
context and when re-evaluated in the completed scope of S. No
diagnostic is required for a violation of this rule.
Before the typedef, A referred to the ::A, however by using the typedef, you now make A refer to the typedef which is prohibited. However, since no diagnostic is required, clang is also standard conforming.
jogojapan's comment explains the reason for this rule.
Take the following change to your code:
template<class T>
class A
{};
template<class T>
class B
{
public:
A a; // <-- What "A" is this referring to?
typedef A<T> A;
};
Because of how class scope works, A a; becomes ambiguous.
I will add to Jesse's answer about the seemingly peculiar behavior of GCC in compiling:
typedef A<T> A;
vs
typedef ::A<T> A;
This also applies to using statements as well of the form:
using A = A<T>;
using A = ::A<T>;
What seems to be happening within GCC, is that during the compilation of the typedef/using statement declaring B::A, that the symbol B::A becomes a valid candidate within the using statement itself. I.e. when saying using A = A<T>; or typedef A<T> A; GCC considers both ::A and B::A valid candidates for A<T>.
This seems odd behavior because as your question implies, you don't expect the new alias A to become a valid candidate within the typedef itself, but as Jesse's answer also says, anything declared within a class becomes visible to everything else inside the class - and in this case apparently even the declaration itself. This type of behavior may be implemented this way to permit recursive type definitions.
The solution as you found is to specify for GCC precisely which A you're referring to within the typedef and then it no longer complains.