template aliases and sfinae - c++

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 .

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

Should `typename T::value_type` fail to compile when it is meant to be rejected by a constraint that comes later in the definition?

The following code:
#include <concepts>
template <typename T>
struct Foo
{
template <std::convertible_to<typename T::value_type> U> requires requires { typename T::value_type; }
void bar()
{
}
template <typename U>
void bar()
{
}
};
int main()
{
auto foo = Foo<float> {};
foo.bar<int>();
}
is rejected by GCC 11:
error: ‘float’ is not a class, struct, or union type
8 | void bar()
| ^~~
What I expected to happen was that the first definition of bar to be rejected due to an unsatisfied constraint and the second one to be selected. However, apparently when GCC tries to substitute float for T it fails to compile typename T::value_type before looking at the constraint. I can get the behaviour that I expected if I replace the definition with:
template <typename U> requires (requires { typename T::value_type; } && std::convertible_to<U, typename T::value_type>)
void bar()
{
}
which I find much less elegant.
Does the standard say that the first approach is illegal or is it a deficiency in the GCC implementation? If it's the former, is there a nicer way of writing this constraint (short of defining a new named concept like convertible_to_value_type_of)?
Edit: Just to clarify in the light of comments and the (now deleted) answer: I understand why this code would be rejected based on pre-C++20 rules. What I was getting at is that the addition of concepts to C++20 could have been an opportunity to relax the rules so that the compiler defers the verification of validity of something like typename T::value_type until it checks the constraints that might come in the rest of the definition. My question is really: were the rules relaxed in this manner?
The standard is quite clear that constraints are only substituted into at the point of use or when needed for declaration matching:
The type-constraints and requires-clause of a template
specialization or member function are not instantiated along with the
specialization or function itself, even for a member function of a
local class; substitution into the atomic constraints formed from them
is instead performed as specified in [temp.constr.decl] and
[temp.constr.atomic] when determining whether the constraints are
satisfied or as specified in [temp.constr.decl] when comparing
declarations.
This is a GCC bug. It appears that GCC does handle this correctly in a requires-clause so that can be a workaround:
template <class U>
requires std::convertible_to<U, typename T::value_type>
void bar()
{
}

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.

Private member access in template substitution and SFINAE

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

implicit instantiation of undefined template 'class'

When trying to offer functions for const and non-const template arguments in my library I came across a strange problem. The following source code is a minimal example phenomenon:
#include <iostream>
template<typename some_type>
struct some_meta_class;
template<>
struct some_meta_class<int>
{
typedef void type;
};
template<typename some_type>
struct return_type
{
typedef typename some_meta_class< some_type >::type test;
typedef void type;
};
template<typename type>
typename return_type<type>::type foo( type & in )
{
std::cout << "non-const" << std::endl;
}
template<typename type>
void foo( type const & in )
{
std::cout << "const" << std::endl;
}
int main()
{
int i;
int const & ciref = i;
foo(ciref);
}
I tried to implement a non-const version and a const version for foo but unfortunately this code won't compile on CLANG 3.0 and gcc 4.6.3.
main.cpp:18:22: error: implicit instantiation of undefined template
'some_meta_class'
So for some reason the compiler wants to use the non-const version of foo for a const int-reference. This obviously leads to the error above because there is no implementation for some_meta_class. The strange thing is, that if you do one of the following changes, the code compile well and works:
uncomment/remove the non-const version
uncomemnt/remove the typedef of return_type::test
This example is of course minimalistic and pure academic. In my library I came across this problem because the const and non-const version return different types. I managed this problem by using a helper class which is partially specialized.
But why does the example above result in such strange behaviour? Why doesn't the compiler want to use the non-const version where the const version is valid and and matches better?
The reason is the way function call resolution is performed, together with template argument deduction and substitution.
Firstly, name lookup is performed. This gives you two functions with a matching name foo().
Secondly, type deduction is performed: for each of the template functions with a matching name, the compiler tries to deduce the function template arguments which would yield a viable match. The error you get happens in this phase.
Thirdly, overload resolution enters the game. This is only after type deduction has been performed and the signatures of the viable functions for resolving the call have been determined, which makes sense: the compiler can meaningfully resolve your function call only after it has found out the exact signature of all the candidates.
The fact that you get an error related to the non-const overload is not because the compiler chooses it as a most viable candidate for resolving the call (that would be step 3), but because the compiler produces an error while instantiating its return type to determine its signature, during step 2.
It is not entirely obvious why this results in an error though, because one might expect that SFINAE applies (Substitution Failure Is Not An Error). To clarify this, we might consider a simpler example:
template<typename T> struct X { };
template<typename T> typename X<T>::type f(T&) { } // 1
template<typename T> void f(T const&) { } // 2
int main()
{
int const i = 0;
f(i); // Selects overload 2
}
In this example, SFINAE applies: during step 2, the compiler will deduce T for each of the two overloads above, and try to determine their signatures. In case of overload 1, this results in a substitution failure: X<const int> does not define any type (no typedef in X). However, due to SFINAE, the compiler simply discards it and finds that overload 2 is a viable match. Thus, it picks it.
Let's now change the example slightly in a way that mirrors your example:
template<typename T> struct X { };
template<typename Y>
struct R { typedef typename X<Y>::type type; };
// Notice the small change from X<T> into R<T>!
template<typename T> typename R<T>::type f(T&) { } // 1
template<typename T> void f(T const&) { } // 2
int main()
{
int const i = 0;
f(i); // ERROR! Cannot instantiate R<int const>
}
What has changed is that overload 1 no longer returns X<T>::type, but rather R<T>::type. This is in turn the same as X<T>::type because of the typedef declaration in R, so one might expect it to yield the same result. However, in this case you get a compilation error. Why?
The Standard has the answer (Paragraph 14.8.3/8):
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 if written using the substituted arguments. [...] Only invalid types and expressions in the immediate context of the function type and its template parameter types can result in a deduction failure.
Clearly, the second example (as well as yours) generates an error in a nested context, so SFINAE does not apply. I believe this answers your question.
By the way, it is interesting to notice, that this has changed since C++03, which more generally stated (Paragraph 14.8.2/2):
[...] If a substitution in a template parameter or in the function type of the function template results in an invalid type, type deduction fails. [...]
If you are curious about the reasons why things have changed, this paper might give you an idea.