2

Consider the following example, consisting of 4 files.



Outer.h

#pragma once

#include "genericAlgorithm.h"

class Outer
{
private:
    struct Inner {};    // Note that Inner is private

    const Inner inner;

public:
    Outer() : inner() {}

    inline void method()
    {
        genericAlgorithm(inner);
    }
};


genericAlgorithm.h

#pragma once

template <typename T>
void genericAlgorithm(const T& value);


genericAlgorithm.cpp

#include "genericAlgorithm.h"

#include "Outer.h"

template <typename T>
void genericAlgorithm(const T& value) {}

// Explicit template instantiation (compiles on GCC, Clang; error C2248 on MSVC)
template void genericAlgorithm<Outer::Inner>(const Outer::Inner& value);


main.cpp

#include "Outer.h"

int main()
{
    Outer outer;
    outer.method();
    return 0;
}

As you can see, in genericAlgorithm.cpp there is an explicit instantiation of genericAlgorithm() function template for argument Outer::Inner, which is a private inner struct of class Outer.

It is my understanding that this is legal, since, according to cppreference.com...

Explicit instantiation definitions ignore member access specifiers: parameter types and return types may be private.

And in fact, this code compiles perfectly fine on GCC 6.3 and Clang 4.0.

However, MSVC (Visual Studio 2017 15.2) seems to take issue with it and produces the following compilation error:

genericalgorithm.cpp(9): error C2248: 'Outer::Inner': cannot access private struct declared in class 'Outer'

So, is this a bug in MSVC or am I missing something and in fact there is a problem with my code that needs to be fixed? If so, does it mean that it's GCC and Clang, along with cppreference.com, who are wrong?


UPDATE: I believe I found a relevant passage in §14.7.2 [templ.explicit] (item 12) of n4296 and n4567 working drafts of the standard:

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]

Unless I'm misinterpreting what's written, it seems that this behaviour of MSVC is indeed non-compliant. Granted, these are just drafts - I unfortunately don't have access to the actual $133 per copy standard, so I can't be sure if this item has been retained.

TerraPass
  • 1,562
  • 11
  • 21

1 Answers1

0

I would read that to mean that the explicit instantiation definition ignores access specifiers in the thing you are explicitly specifying. It doesn't magically grant you access to private members of other items.

Type "Inner" is private to the thing you are using as an argument, not to type genericAlgorithm

Joe
  • 5,394
  • 3
  • 23
  • 54
  • So you are saying that both GCC and Clang got it wrong, since they compile the given example without any issues? – TerraPass Nov 06 '17 at 19:00
  • I admit I wish I'd written my "answer" as a comment since it was more me speculating than certainty. It makes no sense to me that you would be granted access to private members of other types merely because you are specializing a template. But I'm well aware of the face that GCC tends to be better standards-compliant than VC. – Joe Nov 06 '17 at 19:06
  • I'm *explicitly instantiating*, however, not *specializing* a template. The use case, where I actually encountered this problem, was in a project, where to reduce (re)compilation times some template implementations were moved to .cpp's and corresponding templates were instantiated explicitly. One of these instantiations involved a private type, like in this example. To me it seemed very helpful to ignore access specifiers in this case, because the alternative would be to invade on the encapsulation of the class, containing the private type, by making it public or adding template as a friend. – TerraPass Nov 06 '17 at 19:20
  • Note that while the template was *implicitly* instantiated, no such concessions to lift the access restrictions had to be made. So it makes sense to me that explicit instantiation shouldn't introduce additional obstacles, compared to implicit one. Therefore, I have a strong suspicion that in this case MSVC is being non-compliant. – TerraPass Nov 06 '17 at 19:21
  • Let me make sure I understand you. Are you saying that when genericAlgorithm (or whatever it stands for in your real project) was merely a template in a header file, you were actually able to write it so that it took an argument that was a private type declared in some other, unrelated class? And that compiled? – Joe Nov 06 '17 at 19:58
  • Precisely. And it made perfect sense too: it did not require any changes to the class, which actually used this template: just as it previously implicitly instantiated said template with its own private type, so it was able to now utilize this instantiation without its encapsulation having to be compromised. – TerraPass Nov 06 '17 at 20:06
  • Oh, you mean before it was explicitly instantiated? Then, even more "yes". :) I mean, if using private types as template type arguments was disallowed in general, you wouldn't be able to have `std::vector`, for example, as one of `Outer`'s fields, which would be absurd. – TerraPass Nov 06 '17 at 20:11
  • 1
    Using private types as template arguments is not disallowed as long as you are meeting the rules of... privacy. That is, it is perfectly fine to declare a std::vector from within some member function of class Outer. But it is *not* valid to declare it anywhere outside the class. A template function -- outside of the class -- which therefore does that should likewise not be valid. Your template function not only uses Outer::Inner as a template argument (which is OK) but it also takes an Outer::Inner as a function argument, which is *not* OK – Joe Nov 06 '17 at 21:13
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/158351/discussion-between-terrapass-and-joe). – TerraPass Nov 06 '17 at 21:16