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.
Related
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
I am looking into class template deduction available since C++17. Here are the code I would like to ask about:
#include <iostream>
#include <cmath>
using std::endl;
using std::cout;
template<typename T>
struct MyAbs {
template<typename U>
MyAbs(U&& u) : t(std::forward<T>(u))
{ cout << "template" << endl;}
#ifdef ON
MyAbs(const T& t) : t(t) {}
#endif
T operator()() const
{
return std::abs(t);
}
T t;
};
/*
// may need the following
template<typename U>
MyAbs(U&&) -> MyAbs<typename std::remove_reference<U>::type>;
*/
int main()
{
const double d = 3.14;
cout << MyAbs(4.7)() << endl;
cout << MyAbs(d)() << endl;
return 0;
}
When MyAbs(const T&) is not conditionally-compiled (i.e. no -DON), both clang++ and g++ fail to deduce the template parameter, T. Given -DON=1, both compilers build the simple example above.
Firstly, I also guessed that the deduction should fail; the compiler could deduce U but not T. The compile errors I got were what I expected. Please, let me know if I am mistaken.
If I was correct on it, then, I cannot understand why the deduction with U&& succeeds when MyAbs(const T&) is added. What I expected was deducing with U&& fails, and SFINAE allows me to invoke MyAbs(const T&) instead for both cases: 4.7 and d. However, what happened is different. This program seems to invoke the template version for 4.7 and non-template version for d.
$ g++ -Wall -std=c++17 ~/a.cc -DON=1
$ ./a.out
template
4.7
3.14
It seems that the template version has suddenly become viable. Is this expected? If so, what's the reason?
Class template argument deduction happens in two stages.
Deduce the class template arguments.
Then, do the actual construction with the concrete class type.
Step 1 only helps you figure out the class template arguments. It does not do anything with regards to the actual constructor (or class template specialization) that may be used in step 2.
The existence of the constructors might drive the deduction, but if a given constructor is used to deduce that says nothing about whether or not it's used to construct.
So, when you just have:
template<typename T>
struct MyAbs {
template<typename U>
MyAbs(U&& u);
};
Class template argument deduction fails - you have no deduction guide in your constructor, T is a non-deduced context. The compiler just can't figure out what T you want in MyAbs(4.7) or MyAbs(d).
When you added this one:
template<typename T>
struct MyAbs {
template<typename U>
MyAbs(U&& u);
MyAbs(T const&);
};
Now it can! In both cases, it deduces T as double. And once it does that, then we go ahead and perform overload resolution as if we had typed MyAbs<double> to begin with.
And here, MyAbs<double>(4.7) happens to prefer the forwarding reference constructor (less cv-qualified reference) while MyAbs<double>(d) happens to prefer the other one (non-template preferred to template). And that's okay and expected, just because we used one constructor for deduction doesn't mean we have to use specifically that constructor for construction.
This code compiles just fine:
template <typename T1>
struct Struct {
};
struct ConvertsToStruct {
operator Struct<int>() const;
};
template <typename T>
void NonVariadicFunc(Struct<T>);
int main() {
NonVariadicFunc<int>(ConvertsToStruct{});
return 0;
}
But an attempt to make it a little more generic, by using variadic templates, fails to compile:
template <typename T1>
struct Struct {
};
struct ConvertsToStruct {
operator Struct<int>() const;
};
template <typename... T>
void VariadicFunc(Struct<T...>);
int main() {
VariadicFunc<int>(ConvertsToStruct{});
return 0;
}
What's going wrong? Why isn't my attempt to explicitly specify VariadicFunc's template type succeeding?
Godbolt link => https://godbolt.org/g/kq9d7L
There are 2 reasons to explain why this code can't compile.
The first is, the template parameter of a template function can be partially specified:
template<class U, class V> void foo(V v) {}
int main() {
foo<double>(12);
}
This code works, because you specify the first template parameter U and let the compiler determine the second parameter. For the same reason, your VariadicFunc<int>(ConvertsToStruct{}); also requires template argument deduction. Here is a similar example, it compiles:
template<class... U> void bar(U... u) {}
int main() {
bar<int>(12.0, 13.4f);
}
Now we know compiler needs to do deduction for your code, then comes the second part: compiler processes different stages in a fixed order:
cppreference
Template argument deduction takes place after the function template name lookup (which may involve argument-dependent lookup) and before template argument substitution (which may involve SFINAE) and overload resolution.
Implicit conversion takes place at overload resolution, after template argument deduction. Thus in your case, the existence of a user-defined conversion operator has no effect when compiler is doing template argument deduction. Obviously ConvertsToStruct itself cannot match anything, thus deduction failed and the code can't compile.
The problem is that with
VariadicFunc<int>(ConvertsToStruct{});
you fix only the first template parameter in the list T....
And the compiler doesn't know how to deduce the remaining.
Even weirder, I can take the address of the function, and then it works
It's because with (&VariadicFunc<int>) you ask for the pointer of the function (without asking the compiler to deduce the types from the argument) so the <int> part fix all template parameters.
When you pass the ConvertToStruct{} part
(&VariadicFunc<int>)(ConvertToStruct{});
the compiler know that T... is int and look if can obtain a Struct<int> from a ConvertToStruct and find the apposite conversion operator.
This is the code:
#include <iostream>
#include <type_traits>
template <class T>
typename std::enable_if<std::is_integral<T>::value,bool>::type
is_odd (T i) {return bool(i%2);}
// 2. the second template argument is only valid if T is an integral type:
template < class T,
class = typename std::enable_if<std::is_integral<T>::value>::type>
bool is_even (T i) {return !bool(i%2);}
int main() {
short int i = 1; // code does not compile if type of i is not integral
std::cout << std::boolalpha;
std::cout << "i is odd: " << is_odd(i) << std::endl;
std::cout << "i is even: " << is_even(i) << std::endl;
return 0;
}
I am trying to learn proper usage of enable_if, i understand if it's used as a return type specifier is that: the compiler will ignore the code. Meaning, the function won't be in the binary file.
I am somehow confused if it's used in a template argument. Based from the code above, it says there that the second template argument is only valid if T is an integral type But i am confused what is the purpose of that second argument?
I removed it and changed it to:
template < class T>
bool is_even (T i) {return !bool(i%2);}
and it still works fine. Can someone clarify me what's the real purpose of it? There's no variable specifier on it as well.
Or maybe it only serves as a checker if ill do something like
template < class T,
class B= typename std::enable_if<std::is_integral<T>::value>::type>
Allowing me to access B on my code(can be true or false) ?
You are using it with an integral type. It will only fail when used with a non-integral type. The following should fail to compile:
float f;
is_even(f);
Note that with a C++14 compiler you can write:
template <class T,
class = std::enable_if_t<std::is_integral<T>::value>>
However, you may want to use a static_assert here, since the function probably does not make sense for non integral types, and it will give a better error message at compile time.
Another usage of additional parameters in SFINAE is to lower the preference for a given function. The template with more template arguments with be less likely to get selected than the one with less templates arguments.
The purpose of the enable_if in this case is to cause a compilation error when the deduced template argument for T is not an integral type. We can see from cppreference/is_integral that integral types are integers, characters and their signed and unsigned variants.
For any other type you will get an error that looks like:
main.cpp:21:32: error: no matching function for call to 'is_odd'
std::cout << "i is odd: " << is_odd(NotIntegral()) << std::endl;
^~~~~~
main.cpp:6:25: note: candidate template ignored: disabled by 'enable_if' [with T = NotIntegral]
typename std::enable_if<std::is_integral<T>::value,bool>::type
I understand if it's used as a return type specifier is that the compiler will ignore the code
This isn't true. The return type is evaluated just like any other part of the declaration. See Why should I avoid std::enable_if in function signatures. The choice of placement of the std::enable_if has its pros and cons.
But i am confused what is the purpose of that second argument?
Consider the example where you have a function named foo that takes some T.
template<class T> void foo(T);
You want to restrict one overload of foo whose T's have a value member equal to 1, and want to use another overload for when T::value does not equal 1. If we simply have this:
template<class T> void foo(T); // overload for T::value == 1
template<class T> void foo(T); // overload for T::value != 1
How does this convey to the compiler that you want to use two separate overloads for two separate things? It doesn't. They are both ambiguous function calls:
template<std::size_t N>
struct Widget : std::integral_constant<std::size_t, N> { };
int main() {
Widget<1> w1;
Widget<2> w2;
foo(w1); // Error! Ambiguous!
foo(w2); // Error! Ambiguous!
}
You will need to use SFINAE to reject the templates based on our condition:
template<class T, class = std::enable_if_t<T::value == 1>* = nullptr>
void foo(T); // #1
template<class T, class = std::enable_if_t<T::value != 1>* = nullptr>
void foo(T); // #2
Now the correct ones are called:
foo(w1); // OK! Chooses #1
foo(w2); // OK! Chooses #2
What makes this work is the way std::enable_if is built. If the condition in its first parameter is true, it provides a member typedef named type equal to the second parameter (defaulted to void). Otherwise, if the condition is false, it does not provided one. A reasonable implementation could be:
template<bool, class R = void>
struct enable_if { using type = R; };
template<class R>
struct enable_if<false, R> { /* empty */ };
So if the condition fails, we will be trying to access a ::type member that doesn't exist (remember that std::enable_if_t is an alias for std::enable_if<...>::type). The code would be ill-formed, but instead of it causing a hard error, during overload resolution the template is simply rejected from the candidate set and other overloads (if present) are used instead.
This is a simple explanation of SFINAE. See the cppreference page for more information.
In your case there is only one overload of is_odd and is_even, so if you pass NotIntegral to them, they will fail because their respective overloads are taken out of the candidate set, and since there are no more overloads to evaluate overload resolution fails with the aforementioned error. This could be cleaned up a bit with a static_assert message instead.
template<class T>
bool is_even (T i) {
static_assert(std::is_integral<T>::value, "T must be an integral type");
return !bool(i%2);
}
Now you will no longer be given a weird looking error message but your own custom one. This also rules out SFINAE since the static_assert is evaluated after template arguments are substituted, so it's your choice on which route to take. I would recommend the assertion since it's much cleaner and there is no need for SFINAE here.
The following code compiles just fine under Visual Studio but neither gcc 4.6.2 or 4.7 can handle it. It seems to be valid but gcc can't seem to resolve the difference between const and non const parameters. Could this be a compiler bug?
struct CReadType{};
struct CWriteType{};
template<typename ReadWriteType, typename T>
struct AddPkgrConstByType {};
template<typename T>
struct AddPkgrConstByType<CReadType, T> {
typedef T type;
};
template<typename T>
struct AddPkgrConstByType<CReadType, const T> {
typedef T type;
};
template<typename T>
struct AddPkgrConstByType<CWriteType, T> {
typedef T const type;
};
template<typename Packager, typename T>
struct AddPkgrConst : public AddPkgrConstByType<typename Packager::CReadWriteType, T> {
};
template<typename Packager, typename T>
inline bool Package( Packager* ppkgr, T* pt )
{
return true;
}
template<typename Packager>
inline bool Package( Packager* ppkgr, typename AddPkgrConst<Packager,bool>::type* pb)
{
return false;
}
struct ReadPackager {
typedef CReadType CReadWriteType;
};
struct WritePackager {
typedef CWriteType CReadWriteType;
};
int main(int argc, char* argv[])
{
ReadPackager rp;
WritePackager wp;
bool b = true;
const bool cb = false;
Package( &rp, &b );
}
Compiler call:
g++ -fPIC -O -std=c++0x -Wno-deprecated -D_REENTRANT
g++-D__STDC_LIMIT_MACROS -c test.cpp
test.cpp: In function ‘int main(int, char**)’:
test.cpp:58:22: error: call of overloaded ‘Package(ReadPackager*, bool*)’ is ambiguous
test.cpp:58:22: note: candidates are:
test.cpp:31:6: note: bool Package(Packager*, T*) [with Packager = ReadPackager, T = bool]
test.cpp:38:6: note: bool Package(Packager*, typename AddPkgrConst<Packager, bool>::type*) [with Packager = ReadPackager, typename AddPkgrConst<Packager, bool>::type = bool]
This looks like a compiler error to me. The issues involved here are overload resolution and partial ordering of template functions. Since both template functions can match the argument list (ReadPackager*, bool), partial ordering of template functions should be used to choose the more specialized template function.
Put simply, a template function is at least as specialized as another if the arguments to that function can always be used as arguments to the other.
It's clear that any two pointer arguments match the first Package() function, but for instance Package(ReadPackager*, const int*) can not match the second. This seems to imply that the second Package function is more specialized and ought to resolve any ambiguity.
However, since there is disagreement among the compilers, there may be some subtleties involved that are overlooked by the simplified explanation. I will therefore follow the procedure for determining function template partial ordering from the standard to discern the correct behavior.
First, labeling the functions as P1 and P2 for easy reference.
P1:
template<typename Packager, typename T>
bool Package( Packager* ppkgr, T* pt );
P2:
template<typename Packager>
bool Package( Packager* ppkgr, typename AddPkgrConst<Packager,bool>::type* pb);
The standard says that for each template function (T1), we must generic unique type for each of its template parameters, use those types to determine the function call parameter types, and then use those types to deduce the types in the other template (T2). If this succeeds, the first template (T1) is at least as specialized as the second (T2).
First P2->P1
Synthesize unique type U for template parameter Packager of P2.
Perform type deduction against P1's parameter list. Packager is deduced to be U and T is deduced to be AddPkgrConst<Packager,U>::type.
This succeeds and P1 is judged to be no more specialized than P2.
Now P1->P2:
Synthesize unique types U1 and U2 for template parameters Packager and T of P1 to get the parameter list (U1*, U2*).
Perform type deduction against P2's parameter list. Packager is deduced to be U1.
No deduction is performed for the second parameter because, being a dependent type, it is considered a non-deduced context.
The second argument is therefore AddPkgrConst<U1,bool>::type which evaluates to bool. This does not match the second parameter U2.
This procedure fails if we proceed to step 4. However, my suspicion is that the compilers that reject this code don't perform step 4 and therefore consider P2 no more specialized than P1 merely because type deduction succeeded. This seems counter intuitive since P1 clearly accepts any input that P2 does and not vice versa. This part of the standard is somewhat convoluted, so it's not clear whether this final comparison is required to be made.
Let's try to address this question by applying §14.8.2.5, paragraph 1, Deducing template arguments from a type
Template arguments can be deduced in several different contexts, but in each case a type that is specified in terms of template parameters (call it P) is compared with an actual type (call it A), and an attempt is made to find template argument values (a type for a type parameter, a value for a non-type parameter, or a template for a template parameter) that will make P, after substitution of the deduced values (call it the deduced A), compatible with A.
In our type deduction, the deduced A is AddPkgrConst<U1,bool>::type=bool. This is not compatible with the original A, which is the unique type U2. This seems to support the position that the partial ordering resolves the ambiguity.
I don't know what's wrong with Visual Studio, but what gcc says seems right:
You instantiate AddPkgrConstByType<CReadType, T> because Packager::CReadWriteType resolves to CReadType. Therefore, AddPkgrConst<Packager,bool>::type will resolve according to the first implementation (which is not a specialisation) to bool. This means you have two separate function specialisations with the same parameter list, which C++ doesn't allow you.
Since function templates can't be specialized, what you have here is two function template overloads. Both of these overloads are capable of accepting a bool* as their second argument so they appear to properly be detected as ambiguous by g++.
However classes can be partially specialized so that only one version will be picked, and you can use wrapper magic to attain your desired goal. I'm only pasting the code I added.
template <typename Packager, typename T>
struct Wrapper
{
static bool Package()
{
return true;
}
};
template <typename Packager>
struct Wrapper<Packager, typename AddPkgrConst<Packager,bool>::type>
{
static bool Package()
{
return false;
}
};
template <typename Packager, typename T>
Wrapper<Packager, T> make_wrapper(Packager* /*p*/, T* /*t*/)
{
return Wrapper<Packager, T>();
}
int main()
{
ReadPackager rp;
bool b = true;
std::cout << make_wrapper(&rp, &b).Package() << std::endl; // Prints out 0.
}