In the following (minimized) code, I have a public using declaration that is referring to decltype(something_private): using Foo = decltype(something_private<T>).
On Clang but not GCC this does not compile because of it being private.
Questions:
What is an elegant solution if I do not want to make func<T>() public.
Where in the C++ standard (C++11) does is backup Clang to be correct here?
The below code fails with the following error code on Clang (3.9 - 7.0) but builds on GCC (4.8.4 - 8.2):
class A {
private:
template <class T>
static auto func() -> T; // The actual return type is much
// more complicated, so `using Foo = T` would not work.
public:
template <class T>
using Foo = decltype(func<T>());
};
int main(int, char**) {
A::Foo<int> y;
return y;
}
Clang 7.0 output:
<source>:10:24: error: 'func' is a private member of 'A'
using Foo = decltype(func<T>());
^~~~~~~
<source>:14:7: note: in instantiation of template type alias 'Foo' requested here
A::Foo<int> y;
^
<source>:6:15: note: declared private here
static auto func() -> T;
^
1 error generated.
Compiler returned: 1
https://godbolt.org/z/zD4Hk5
I haven't looked in the standard for citation, but have a workaround for you. Because this works, it makes me think clang just has a bug. When the function is directly in A, it treats the type alias as if it were in the context of the caller, but moving the function into a struct solves that. Meh. I've done a lot of g++ / clang porting lately and while I didn't run into this specifically, it smelled of some things I encountered.
class A {
private:
struct X {
template <class T>
static auto func() -> T;
};
public:
template <class T>
using Foo = decltype(X::func<T>());
};
void bar() {
A::Foo<int> y;
}
https://godbolt.org/z/ozIS-r
UPDATE: added citation.
I think this directly answers your question, and that clang is wrong here.
N4778 (the latest I found), 10.8/p4 (pg 259) ... [Note: Because access control applies to names, if access control is applied to a typedef name, only the accessibility of the typedef name itself is considered. The accessibility of the entity referred to by the typedef is not considered.
Related
Consider following code:
template <typename T> struct X
{
X(T) {}
void foo() {}
};
template <typename T> struct Y
{
int object = 0;
void bar()
{
X(object).foo();
}
};
Live on gcc.godbold.org
GCC 8.2 compiles it, while Clang 7 spits out following error:
<source>:13:18: error: member reference base type 'X' is not a structure or union
X(object).foo();
~~~~~~~~~^~~~
This looks like a bug to me.
The conditions are very specific: If either structure is not a template, or if object is not a member variable, or if CTAD (class template argument deduction) is not involved, then Clang compiles the code as well.
What's going on here? Is it indeed a Clang bug?
And what's more important, how can I make the code compile with minimal changes, preferrably without getting rid of CTAD?
The only flag used is -std=c++17.
clang++ --version is
clang version 7.0.0 (tags/RELEASE_700/final 342594)
Target: x86_64-unknown-linux-gnu
Thread model: posix
Yes, this is clang bug see Class template argument deduction with deduced type fails when accessing member which says:
Try to compile the following c++ program:
template <class T>
struct C {
C(T) {}
int a;
};
template <class T>
int foo(T v) {
return C{v}.a; // <----
}
int main() {
foo(4);
}
The line marked above fails with the error:
error: member reference base type 'C' is not a structure or union
return (C{v}).a;
~~~~~~^~
The bug report also specifies cases that do work, which may or may not be alternatives.
Note that the following all work fine:
template <class T>
C<T> foo(T v) {
return C{v};
}
and
int foo(int v) {
return C{v}.a;
}
and
C{4}.a;
I tried this also on a recent trunk build (trunk 346600)
According to [temp.deduct.guide/3]:
(...) A deduction-guide shall be declared in the same scope as the
corresponding class template and, for a member class template, with
the same access. (...)
But below example doesn't seem to compile in both [gcc] and [clang].
#include <string>
template <class>
struct Foo {
template <class T>
struct Bar {
Bar(T) { }
};
Bar(char const*) -> Bar<std::string>;
};
int main() {
Foo<int>::Bar bar("abc");
static_cast<void>(bar);
}
What is the correct syntax of deduction guide for nested template class? Or maybe this one is correct but it isn't yet supported by the compilers?
Similar syntax but without nested class compiles fine both in gcc and clang:
#include <string>
template <class T>
struct Bar {
Bar(T) { }
};
Bar(char const*) -> Bar<std::string>;
int main() {
Bar bar("abc");
static_cast<void>(bar);
}
[temp.deduct.guide] includes the sentence:
A deduction-guide shall be declared in the same scope as the corresponding class template and, for a member class template, with the same access.
This suggests that your example should work - deduction guides are explicitly supported for member class templates, as long as they're declared in the same scope and access (which would be the class scope and public - check and check).
This is gcc bug 79501 (filed by Richard Smith).
If you really need a temporary quick-fix, consider using compiler-specific instructions.
Here, on godbolt
The key is to handle the behavioral divergence between GCC and Clang by adding a compiler-specific directive.
This is way sub-optimal, but if you are blocked in your project, it may help you wait for compiler(s) patch.
See my answer to this post : https://stackoverflow.com/a/66888013/10883423
#include <string>
template <class>
struct Foo {
template <class T>
struct Bar {
Bar(T) { }
};
#if __clang__
Bar(char const*) -> Bar<std::string>;
#endif
};
void instanciate_symbols()
{
[[maybe_unused]] auto bar = Foo<int>::Bar{"abc"};
}
I don't understand why in the following code, I am allowed to create the function print_private_template while the compiler complains about print_private_class:
#include <cstdio>
class A
{
private:
template <unsigned T>
struct B
{
};
struct C
{
};
public:
template <unsigned T>
B<T> getAb()
{
return B<T>();
}
C getAc()
{
return C();
}
};
template<unsigned T>
void print_private_template(const A::B<T> &ab)
{
printf("%d\n", T);
}
void print_private_class(const A::C &ac)
{
printf("something\n");
}
int main(int, char**)
{
A a;
print_private_template(a.getAb<42>());
print_private_class(a.getAc());
return 0;
}
Is this an expected behaviour? a compiler bug/extension?
Just to be clear, my goal is to make the compiler error on both the usage of print_private_template and print_private_class.
Comeau does give an error (when you comment out the print_private_class function and its call in strict C++03 mode.
ComeauTest.c(31): error: class template "A::B" (declared at line 7) is inaccessible
void print_private_template(const A::B &ab)
^
detected during instantiation of "print_private_template" based on
template argument <42U> at line 45
G++ 4.5 on Windows does not report any error with -std=c++ -Wall -pedantic though.
Your class A::C and class template A::B<T> both have the same visibility as any other normal members. Hence, both print_private_class and print_private_template require a diagnostic.
11.8 Nested classes [class.access.nest]
1 A nested class is a member and as such has the same access rights as any other member. The members of
an enclosing class have no special access to members of a nested class; the usual access rules (Clause 11)
shall be obeyed.
As stated by Dirk Gently, GCC doesn't perform access control when instantiating template structs / classes nested in other (template) structs / classes.
One way to work around this is to encapsulate them in a non-template struct:
template<int I> class MyTemplate
{
struct PT
{
template<int, typename = void> struct InnerTemplate;
// ... specialisations here ...
};
public:
typedef typename PT::template InnerTemplate<I>::SomeType SomeType;
};
typedef MyTemplate<1>::PT::InnerTemplate<1> ThisWontWork;
The last line will fail to compile with the error:
error: 'struct MyTemplate<1>::PT' is private within this context
I'll grant that this is ugly, especially having to use PT::template but it seems to effectively prevent clients from instantiating helper templates they aren't meant to access, so it's worth a shot.
It got fixed for GCC 11
Ten years later... and the bug got fixed for GCC 11: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=41437#c13 (another point in the dupe pool had been previously linked to by dirkgently).
A minimal reproduction:
main.cpp
class Out {
protected:
class In {};
};
template <class C>
void f() { Out::In in; }
int main() {
f<Out>();
}
still compiles in GCC 10.2 with all warnings enabled:
g++-10 -ggdb3 -O0 -std=c++11 -Wall -Wextra -pedantic -o main.out main.cpp
but fails correctly in clang++ 10:
void f() { Out::In in; }
^
main.cpp:3:11: note: declared protected here
class In {};
^
1 error generated
The above fails to fail in GCC because f is a template function.
The following code doesn't compile, unless the commented line is uncommented:
template <class T> struct R { static T& r(); };
struct M {
static char m(M&);
template <class T> static int m(const T&);
};
template <class T> struct A;
template <class T>
struct B {
struct U { };
struct V { M& operator,(U); };
enum { v = sizeof(M::m((R<V>::r(), R<A<T>*>::r()))) };
};
template <class T> struct A { B<T> y; };
int main()
{
// A<int>(); // Works if uncommented.
B<int>();
}
At the comma operator, the compiler thinks that it needs A<int> to be complete, even though the code only traffics in A<T>*. I don't understand why. It fails with both clang and g++. Clang says
h.cpp:13:36: error: field has incomplete type 'B<int>'
template <class T> struct A { B<T> y; };
^
h.cpp:11:38: note: in instantiation of template class 'A<int>' requested here
enum { v = sizeof(M::m((R<V>::r(), R<A<T>*>::r()))) };
^
h.cpp:17:5: note: in instantiation of template class 'B<int>' requested here
B<int>();
^
h.cpp:8:8: note: definition of 'B<int>' is not complete until the closing '}'
struct B {
^
1 error generated.
I'm now sitting in the debugger debugging the compiler :-) I think what's happening is that the compiler is using argument-dependent lookup to find matching operator,s and the associated classes and namespaces for a pointer to a class include the class itself, so the compiler wants the class to be complete. Maybe.
you get the same error message if you change main to be this:
int main()
{
// A<int>(); // Works if uncommented.
auto& x = R<A<int>*>::r();
B<int>();
}
I'm going to go out on a limb here and say that it's as you hinted. Mentioning a pointer to an A<int> does not cause a template expansion of A<int>.
This makes sense (to me), since it's the same as mentioning a pointer to a forward-declared type - you don't need the type fully defined at that point.
Perhaps someone wiser than me can find the passage in the standard that mandates this.
EDIT: This is not a bug, just me not knowing about dependent name lookups in templated base classes (which MSVC "helpfully" resolves without errors).
I wrote a functor implementation a while back, and a simple "Event" wrapper that uses it. It compiles fine under MSVC, but GCC gives an error about a member variable in the base class, subscribers, not being declared; changing subscribers to this->subscribers resolves the issue(!). It appears to happen only with the curiously recurring template pattern, and with partial template specialization.
Simplified source (sorry for the mind-bending template usage...):
#include <vector>
template<typename TEvent>
struct EventBase
{
protected:
std::vector<int> subscribers;
};
template<typename TArg1 = void, typename TArg2 = void>
struct Event : public EventBase<Event<TArg1, TArg2> >
{
void trigger(TArg1 arg1, TArg2 arg2) const
{
// Error on next line
auto it = subscribers.cbegin();
}
};
template<typename TArg1>
struct Event<TArg1, void> : public EventBase<Event<TArg1> >
{
void trigger(TArg1 arg1) const
{
// Using `this` fixes error(?!)
auto it = this->subscribers.cbegin();
}
};
template<>
struct Event<void, void> : public EventBase<Event<> >
{
void trigger() const
{
// No error here even without `this`, for some reason!
auto it = subscribers.cbegin();
}
};
int main()
{
return 0;
}
Am I invoking undefined behaviour somewhere? Is my syntax somehow wrong? Is this really a bug in GCC? Is it perhaps a known bug? Any insight would be appreciated!
More details: Compiled using g++ -std=c++11 main.cpp. I'm using GCC version 4.7.2. Exact error message:
main.cpp: In member function ‘void Event<TArg1, TArg2>::trigger(TArg1, TArg2) const’:
main.cpp:17:15: error: ‘subscribers’ was not declared in this scope
This is a bug in MSVC instead. Names from dependent base classes have to be "thisambiguated".
The reason is that unqualified lookup of dependent names proceeds in two phases. During the first phase, the base class is not yet known and the compiler cannot resolve the name. MSVC does not implement two-phase name lookup and delays the lookup until the second phase.
The full specialization
template<>
struct Event<void, void> : public EventBase<Event<> >
{
void trigger() const
{
// No error here even without `this`, for some reason!
auto it = subscribers.cbegin();
}
};
does not suffer from this problem, because both the class and its base are regular classes, not class templates, and there is no template dependency to begin with.
When porting C++ code from MSVC to gcc/Clang, dependent name lookup disambiguation and the template keyword disambiguation (i.e. calling member function template using ::template, ->template or .template syntax) are two of the subtleties that you have to deal with (empty base optimization is another one). For all the Standards compliance rhetoric, this will probably never be fixed for reasons of backwards compatibility.