Private member access in template substitution and SFINAE - c++

class A { int a; };
template<typename, typename = void>
class test {};
template<typename T>
class test<T,decltype(T::a)> {};
int main() { test<A> a; }
The code above compiles without error on clang version 3.8.0-2ubuntu4 (tags/RELEASE_380/final), but fails to compile on g++-5 (Ubuntu 5.4.1-2ubuntu1~16.04) 5.4.1 20160904 and g++-6 (Ubuntu 6.2.0-3ubuntu11~16.04) 6.2.0 20160901 with errors like this:
main.cpp: In function ‘int main()’:
main.cpp:9:22: error: ‘int A::a’ is private within this context
int main() { test<A> a; }
^
main.cpp:1:15: note: declared private here
class A { int a; };
In both cases I compiled with -std=c++11, but the effect is the same for -std=c++14 and -std=c++1z.
Which compiler is correct here? I assumed that, at least since C++11, accessing private members during template substitution should trigger SFINAE, implying that clang is correct and gcc is not. Is there some additional rule I am unaware of?
For referencce, I am thinking of the note in §14.8.2/8 of the current standard draft N4606:
If a substitution results in an invalid type or expression, type
deduction fails. An invalid type or expression is one that would be
ill-formed, with a diagnostic required, if written using the
substituted arguments. [ Note: If no diagnostic is required, the
program is still ill-formed. Access checking is done as part of the
substitution process. — end note ]
Using a member function and a member function pointer instead is accepted by both compilers:
class A { void a() {}; };
template<typename, typename = void>
class test {};
template<typename T>
class test<T,decltype(&T::a)> {};
int main() { test<A> a; }

This is very interesting! I think it's a g++ compiler bug and I think that this is what happens. I've tried several alterations of your code with g++ 4.9.3 and clang 3.7.0 .
Although there are somewhat different rules to function vs class template instantiation, I believe that these are the general steps to a template instantiation:
Compiler reads source file with template definitions.
Name lookup (may trigger ADL) : It is the procedure by which a name,
when encountered in a program, is associated with the declaration
that introduced it.
(http://en.cppreference.com/w/cpp/language/lookup)
Template argument specification / deduction : In order to
instantiate a function template, every template argument must be
known, but not every template argument has to be specified. When
possible, the compiler will deduce the missing template arguments
from the function arguments.
(http://en.cppreference.com/w/cpp/language/template_argument_deduction)
Template substitution (may trigger SFINAE) : Every uses of a
template parameter in the function parameter list is replaced with
the corresponding template arguments.Substitution failure (that is,
failure to replace template parameters with the deduced or provided
template arguments) of a function template removes the function
template from the overload set. (http://en.cppreference.com/w/cpp/language/function_template#Template_argument_substitution)
Forming the overload set: Before overload resolution begins, the
functions selected by name lookup and template argument deduction
are combined to form the set of candidate functions.
(http://en.cppreference.com/w/cpp/language/overload_resolution#Details)
Overload resolution : In general, the candidate function whose
parameters match the arguments most closely is the one that is
called.
(http://en.cppreference.com/w/cpp/language/overload_resolution)
Template Instantiation : the template arguments must be determined
so that the compiler can generate an actual function (or class, from
a class template). (http://en.cppreference.com/w/cpp/language/function_template#Function_template_instantiation)
Compiler generates code.
I'll keep these bullet-points as guidelines and references for later on. Furthermore, I'll refer to template evaluation from steps 1-6. If you find anything wrong in the above list please feel free to change it or comment so that I can make the changes.
In the following example:
class A {};
template<typename, typename = void>
struct test
{ test(){std::cout<< "Using None" <<std::endl;} };
template<typename T>
struct test<T, decltype(T::a)>
{ test(){std::cout<< "Using T::a" <<std::endl;} };
int main()
{ test<A> a; }
Output from both compilers:
Using None
This example compiles fine in both g++ and clang, because, when the compiler completes the evaluation process of all the templates, it will only choose to instantiate the first template for being the best match to the template arguments used to create the object in main(). Also, the template substitution process fails when the compiler fails to deduce T::a (SFINAE). Furthermore, due to the argument mismatch, the specialization will not be included in the overload set and will not be instantiated.
Should we add the second template argument, like this:
test<A , decltype(A::a)> a;
The code will not compile and both compilers would complain of:
error: no member named 'a' in 'A'
In the following example however, things start becoming weird:
class A { int a; };
template<typename, typename = void>
struct test
{ test(){std::cout<< "Using None" <<std::endl;} };
template<typename T>
struct test<T, decltype(T::a)>
{ test(){std::cout<< "Using T::a" <<std::endl;} };
int main()
{ test<A> a; }
Output from clang:
Using None
Output from g++:
error: ‘int A::a’ is private
To begin with, I think that this would have been a nice warning. But why an error? The template won't even get instantiated. Considering the previous example, and the fact that pointers-to-members are constant values known at compile time, it seems, that when clang completes the template evaluation stage, with the SFINAE occuring at template substitution, it accurately instantiates the first template and ignores the specialization. But when g++ goes through the substitution process, and looks for the variable T::a, it sees that it is a private member, and instead of saying SFINAE, it prompts with the error above. I think that this is where the bug is, considering this bug report: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61806
Now, the curious part is in the next example, which uses the private member function:
class A{ void a() {}; };
template<typename, typename = void>
struct test
{ test(){std::cout<< "Using None" <<std::endl;} };
template<typename T>
struct test<T,decltype(&T::a)>
{ test(){std::cout<< "Using A::a" <<std::endl;} };
int main()
{ test<A> a; }
Output by both compilers:
Using None
If the previous explanation is true, then why doesn't g++ prompt an error when the private member function is used? Again, this is only an assumption based on the outputs, but I think that this bit actually works as it should. Long story short, SFINAE kicks in, the specialization is discarded from the overload set, and only the first template gets instantiated. Maybe there's more to it than meets the eye, but if we explicitly specify the second template argument, both compilers will prompt the same error.
int main()
{ test<A , decltype(&A::a)> b; }
Output by both compilers:
error: ‘void A::a()’ is private
Anyway, this is the final code I've been using. To demonstrate the outputs, I've made the class public. As an interesting event, I've added a nullptr to point to the member function directly. The type from decltype(((T*)nullptr)->f()) is void, and from the example below, a and c are both invoked by the specialization rather than the first template. The reason is because the second template is more specialized than the first and therefore is the best match for both of them (kill two birds with one stone) (Template Formal Ordering Rules : https://stackoverflow.com/a/9993549/2754510). The type from decltype(&T::f) is M4GolfFvvE (possible translation: Men 4 Golf Fear very vicious Elk), which thanks to boost::typeindex::type_id_with_cvr, it is demangled into void (Golf::*)().
#include <iostream>
#include <boost/type_index.hpp>
class Golf
{
public:
int v;
void f()
{};
};
template<typename T>
using var_t = decltype(T::v);
template<typename T>
using func_t = decltype(&T::f);
//using func_t = decltype(((T*)nullptr)->f()); //void
template<typename, typename = void>
struct test
{
test(){std::cout<< "Using None" <<std::endl;}
};
template<typename T>
struct test<T,var_t<T> >
{
test(){std::cout<< "Using Golf::v" <<std::endl;}
};
template<typename T>
struct test<T,func_t<T> >
{
test(){std::cout<< "Using Golf::f" <<std::endl;}
};
int main()
{
test<Golf> a;
test<Golf,var_t<Golf> > b;
test<Golf,func_t<Golf> > c;
using boost::typeindex::type_id_with_cvr;
std::cout<< typeid(func_t<Golf>).name() << " -> " << type_id_with_cvr<func_t<Golf>>().pretty_name() <<std::endl;
}
Output from both compilers (func_t = decltype(&T::f)):
Using None
Using Golf::v
Using Golf::f
M4GolfFvvE -> void (Golf::*)()
Output from both compilers (func_t = decltype(((T*)nullptr)->f())):
Using Golf::f
Using Golf::v
Using Golf::f
v -> void
Edit:
According to #Dr.Gut (comment below) this bug continues to exist in gcc 9.2. However I found a "hack" around this using std::declval which makes it look even weirder.
#include <utility>
class A
{
int a;
};
template<typename, typename = void>
class test
{};
template<typename T>
class test<T,decltype(std::declval<A>().a)>
{};
int main()
{
test<A> a;
}
Online example: https://rextester.com/BUFU29474
The code compiles and runs fine in g++ and vc++ but fails in clang++.

Related

How to avoid implicit template instantiation of an unused template argument type of a class template when its instance is passed to a function? [duplicate]

I have been experimenting with a system for composable pipelines, which involves a set of 'stages', which may be templated. Each stage handles its own setup, execution and cleanup, and template deduction is used to build a minimal list of 'state' used by the pipeline. This requires quite a lot of boilerplate template code, which has shown up some apparently incongruous behaviour. Despite successful experiments, actually rolling it into our code-base resulted in errors due to invalid instantiations.
It took some time to track down the difference between the toy (working) solution, and the more rich version, but eventually it was narrowed down to an explicit namespace specification.
template<typename KeyType = bool>
struct bind_stage
{
static_assert(!std::is_same<KeyType, bool>::value, "Nope, someone default instantiated me");
};
template<typename BoundStage, typename DefaultStage>
struct test_binding {};
template<template<typename...>class StageTemplate, typename S, typename T>
struct test_binding <StageTemplate<S>, StageTemplate<T>> {};
template<typename T>
auto empty_function(T b) {}
Then our main:
int main()
{
auto binder = test_binding<bind_stage<int>, bind_stage<>>();
//empty_function(binder); // Fails to compile
::empty_function(binder); // Compiles happily
return 0;
}
Now, I'm not sure if I expect the failure, or not. On the one hand, the we create a test_binder<bind_stage<int>,bind_stage<bool>> which obviously includes the invalid instantiation bind_stage<bool> as part of its type definition. Which should fail to compile.
On the other, it's included purely as a name, not a definition. In this situation it could simply be a forward declared template and we'd expect it to work as long as nothing in the outer template actually refers to it specifically.
What I didn't expect was two different behaviours depending on whether I added a (theoretically superfluous) global namespace specifier.
I have tried this code in Visual Studio, Clang and GCC. All have the same behaviour, which makes me lean away from this being a compiler bug. Is this behaviour explained by something in the C++ standard?
EDIT:
Another example from Daniel Langr which makes less sense to me:
template <typename T>
struct X {
static_assert(sizeof(T) == 1, "Why doesn't this happen in both cases?");
};
template <typename T>
struct Y { };
template <typename T>
void f(T) { }
int main() {
auto y = Y<X<int>>{};
// f(y); // triggers static assertion
::f(y); // does not
}
Either X<int> is instantiated while defining Y<X<int>> or it is not. What does using a function in a non-specified scope have to do with anything?
Template are instantiated when needed. So why when one performs a non qualified call as f(Y<X<int>> {}); does the compiler instantiate X<int> while it does not when the call to f is qualified as in ::f(X<Y<int>>{})?
The reason is Agument-Dependent name Lookup(ADL) (see [basic.lookup.argdep]) that only takes place for non qualified calls.
In the case of the call f(Y<X<int>>{}) the compiler must look in the definition of X<int> for a declaration of a friend function:
template <typename T>
struct X {
//such function will participate to the overload resolution
//to determine which function f is called in "f(Y<X<int>>{})"
friend void f(X&){}
};
ADL involving the type of a template argument of the specialization that is the type of the function argument (ouch...) is so miss-loved (because it almost only causes bad surprises) that there is a proposal to remove it: P0934

Is a diagnostic required for unused member templates with ill formed default template parameters?

Consider the following class template:
template<typename T>
struct S
{
template<auto = T()>
void f();
};
Is it ill formed to instantiate S with template parameters T, for which auto = T() is ill formed?
int main()
{
S<int> a; // ok
S<int&> b; // error
S<int()> c; // gcc ok, clang error
}
This seems to be the case, but the issue is with c, where S is instantiated with a function type. gcc is ok with this, while clang says:
error: cannot create object of function type 'int ()'
which makes sense. Since gcc does diagnose the instantiation with int&, I suspect this is a gcc bug. Is that right, or is a diagnostic not required for this code?
This is CWG1635:
1635. How similar are template default arguments to function default arguments?
Default function arguments are instantiated only when needed. Is the same true of default template arguments? For example, is the following well-formed?
#include <type_traits>
template<class T>
struct X {
template<class U = typename T::type>
static void foo(int){}
static void foo(...){}
};
int main(){
X<std::enable_if<false>>::foo(0);
}
Also, is the effect on lookup the same? E.g.,
struct S {
template<typename T = U> void f();
struct U {};
};
Additional note (November, 2020):
Paper P1787R6, adopted at the November, 2020 meeting, partially addresses this issue.

All versions of GCC struggle with a template that has the default type in a definition

I wasted countless hours to pinpoint an issue with gcc. I wanted to test our code base with another compiler to look for more warnings that Clang might have missed. I was shocked that practically half of the project stopped to compile due to failure of template argument deduction. Here I've tried to dumb my case down to the simplest piece of code.
#include <type_traits>
struct Foo
{ };
// This is a template function declaration, where second template argument declared without a default
template <typename T, typename>
void foo(const Foo & foo, T t);
// This is a template function definition; second template argument now has a default declared
template <typename T, typename = typename std::enable_if<1>::type>
void foo(const Foo & foo, T t)
{
}
int main(int argc, char ** argv)
{
foo(Foo{}, 1);
return 0;
}
Ignore a 1 in the std::enable_if<1>. Obviously it's a constant value just to not complicate things when it does not matter.
This piece of code compiles[1] with clang (3.4 through 4.0), icc (16, 17), Visual C++ (19.00.23506). Basically, I couldn't find any other c++11 compiler that, except gcc (4.8 through 7.1), does not compile this piece of code.
The question is, who's right and who's wrong here? Does gcc behave according to the standard?
Obviously this is not a critical issue. I can easily move std::enable_if to the declaration. The only victim would be aesthetics. But it is nice to be able to hide an ugly 100 characters long std::enable_if piece of code, that is not immediately relevant for the user of the library function, in the implementation.
Live example on godbolt.org.
What the standard says ([1] page 350):
The set of default template-arguments available for use with a
template declaration or definition is obtained by merging the default
arguments from the definition (if in scope) and all declarations in
scope in the same way default function arguments are (8.3.6). [
Example:
template<class T1, class T2 = int> class A;
template<class T1 = int, class T2> class A;
is equivalent to
template<class T1 = int, class T2 = int> class A;
— end example ]
So GCC is wrong here. It ignores default template arguments in declarations.
Not all declarations, only function template declarations. Class template declarations are okay:
#include <type_traits>
template <typename T, typename>
struct Foo;
template <typename T, typename = typename std::enable_if<1>::type>
struct Foo
{
T t;
};
int main()
{
Foo<int> foo;
return 0;
}
Live example on godbolt.org
Probably it is due to the nature of how non-default arguments are deduced. In the function template they are deducted from function arguments. In the class template we have to specify them explicitly.
Anyway, I have created a bug report.

template aliases and sfinae

In the case of a substitution failure involving a template alias (e.g. a template alias on a missing member typename, as in the code snippet below), should an error be triggered ?
Clang and gcc seem to disagree on this:
// some types
struct bar { };
struct foo {
typedef void member_type;
};
// template alias
template<class T>
using member = typename T::member_type;
template<class T>
void baz(... ) { }
// only works for gcc, clang fails with: no type named 'member_type'
// in 'bar'
template<class T>
void baz( member<T>* ) { }
int main(int, char** ) {
baz<bar>(0); // picks first
baz<foo>(0); // picks second
return 0;
}
So the question is: who is correct, and why ?
Thanks :-)
According to the Standards, it is clearly GCC that is correct, because the alias template must immediately be replaced and then normal/usual SFINAE is applied to typename T::member_type later when T is known.
But there currently is an issue for this, see http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#1554.
According to the meetings outcome, it appears that clangs behavior is desired: The substitution of T will be done in the context of the alias template (even though at the time of substitution in typename T::member_type, there is no reference to the alias template anymore - it will still need to be referenced as the source of where the parameter type pattern originated from, if this is how it's implemented).
This is similar to another situation where patterns are thrown away on definition time that could influence instantiation semantics
template<int I>
void f(int x[I]);
int main() {
f<0>(nullptr);
}
In this case too, in my opinion the Standard normatively is clear that the parameter is immediately being replaced by int* and thus the instantiation works. See http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#1322 .

recursively nested templates -- gcc bug?

I have the following snippet.
template< typename T >
struct f
{
template< typename V >
struct a : f
{};
};
int main ()
{
f<int>::a<int>::a<double> x;
}
It compiles with no warnings on GCC 4.4.5 and also MSVC 2010, but not on GCC 4.5.2 -- on which I get the following errors:
test.cc: In function 'int main()':
test.cc:11:21: error: expected primary-expression before 'double'
test.cc:11:21: error: expected ';' before 'double'
So while I don't see anything non-standard about it, the question is obligatory -- is this legal in C++? Also, if it is, how do I file a bug report at GCC? (:
edit: A little background for the curious:
This is supposed to be a piece of template metaprogramming. f basically has the structure of a template metafunction class with apply substituted for a (of course the nested type of apply is omitted so we can concentrate on the structure itself).
Inheritance in this case is a standard device for binding metafunction return values. What this snippet is trying to achieve is a metafunction class that recursively yields itself when evaluated.
edit2: let me put the same snippet a bit differently:
template< typename T >
struct f
{
template< typename V > struct a;
};
template< typename T >
template< typename V >
struct f<T>::a : f<T>
{};
int main ()
{
f<int>::a<int>::a<double> x;
}
This produces the same error. I think it refutes the incomplete type argument.
There were a couple of good notes in existing answers. First, f's type is incomplete at the time the nested class template is defined, but f is a dependent type. Now, if you instantiate the nesting template (f), it will instantiate a declaration of the nested template (the member). Note that the declaration of the member does not include the base clause list, so it doesn't need complete base classes. Once the nesting template has been instantiated implicitly, f is complete and when it comes to instantiating the definition of the member, there should be no issue anymore. So I don't think that comeau is correct complaining here.
The other bug is that, in fact, f<int>::a<int>::a is naming the constructor of a<int>, and requires it to be a constructor template (with <int> being the template arguments). The base of this was DR #147.
The translation to the constructor is not done when the qualifier name isn't the class of the injected class name. For example, if it is a derived class, your code becomes valid (as some answers figured out).
Nice question. This seems to be an issue with gcc for template recursive declaration. Because, had there been solid classes then it gives error and ideally it should be declared as:
struct Out {
struct In;
};
struct Out::In : Out {};
it looks like GCC 4.5 believes you have specified the constructor.
one possible workaround:
template<typename T>
struct f {
template<typename V>
struct a : f {
template<typename Z>
struct q {
typedef a<Z> Q;
};
};
};
int main() {
f<int>::a<int>::q<double>::Q x;
return 0;
}
Using Comeau online, it suggests that f is an incomplete type at the point you use it as a base class, and as such is unsuitable.
"ComeauTest.c", line 5: error: incomplete type is not allowed
struct a : f
I don't have a clang instance at hand unfortunately.