Why this template parameters con­straint doesn't work? - templates

Reading templates-revisited:
struct S(T : T*) {
T t; // t is supposed to be of type 'int*', but it's of type 'int', why?
}
void main() {
int x = 123;
S!(int*) s;
static assert(is(typeof(s.t) == typeof(&x)));
}
The above code doesn't compile.
Strangely enough, the following does compile:
struct S(T : int*) {
T t;
}
void main() {
int x = 123;
S!(int*) s;
static assert(is(typeof(s.t) == typeof(&x)));
}
I don't understand this behavior. An explanation would be appreciated.

When a type specialization (the type after the colon) is dependent on the parameter identifier, such as T : T*, the resulting identifier refers to the role of the identifier (T) in the type specialization (the deduced type) if there was a match.
Otherwise, if the specialization is independent, such as T : int*, the resulting identifier is an alias of the type specialization.
Examples:
=========================================================
Argument T | Specialization | Result
=========================================================
void | T : void | void
char | T : void | <no match>
int* | T : T* | int
immutable(char)[] | T : T[] | immutable(char)
immutable(char)[] | T : immutable(T)[] | char
=========================================================
When there is a mismatch for an argument passed to a template parameter, the template is dropped from the overload set. An error is raised if the overload set becomes empty before a match is found.
When there is a mismatch in an IsExpression (the is(...) primary expression), the result is false and no symbols are introduced into scope.

As explained in the Argument Deduction section of http://dlang.org/template.html, when deducing the types of template arguments:
If there is no type spe­cial­iza­tion for the pa­ra­me­ter, the type of the pa­ra­me­ter is set to the tem­plate ar­gu­ment.
If the type spe­cial­iza­tion is de­pen­dent on a type pa­ra­me­ter, the type of that pa­ra­me­ter is set to be the
cor­re­spond­ing part of the type ar­gu­ment.
If after all the type ar­gu­ments are ex­am­ined there are any type pa­ra­me­ters left with no type as­signed, they are as­signed types
cor­re­spond­ing to the tem­plate ar­gu­ment in the same po­si­tion in
the Tem­plateAr­gu­mentList.
If ap­ply­ing the above rules does not re­sult in ex­actly one type for each tem­plate pa­ra­me­ter, then it is an error.
And the example that corresponds to your case is:
template TBar(T : T*) { }
alias TBar!(char*) Foo3; // (2) T is deduced to be char
So, what you're seeing in your first example is expected behavior. Because the T is on both sides, T ends up being evaluated to what would result in the template argument being T*. So, since the template argument is int*, T* would be int*, and T ends up being int. What you have is very similar to std.traits.pointerTarget:
/**
Returns the target type of a pointer.
*/
template pointerTarget(T : T*)
{
alias T pointerTarget;
}
Your second example compiles, because that template is requiring that T be implicitly convertible to int*. And since int* is implicitly convertible to itself, when you pass int* as the template argument, it works. What's causing you trouble is when T is on both sides, because then the right-hand side of the expression is dependent on the left.
Now, I assume that what you actually intend to test here is that the template argument is a pointer? If that's the case, then you should use std.traits.isPointer:
struct S(T)
if(isPointer!T)
{
T t;
}

Related

How template argument deduction is performed in this example?

Consider the following example:
template <class T>
void f(const T&&) { std::cout << __PRETTY_FUNCTION__; };
int main(void){
const int *cptr = nullptr;
f(std::move(cptr));
}
Per [temp.deduct.call]/1:
Template argument deduction is done by comparing each function
template parameter type (call it P) that contains
template-parameters that participate in template argument deduction
with the type of the corresponding argument of the call (call it A)
[..]
and [temp.deduct.call]/3:
If P is a cv-qualified type, the top-level cv-qualifiers of P's type
are ignored for type deduction. If P is a reference type, the type
referred to by P is used for type deduction.
Considering the given paragraphs, I'm deducing the template argument for T to be int* as follows:
P = const T&&, A = const int*; // replacing 'const T&&' with 'const T'
P = const T, A = const int*
T = int*
// what's wrong with these steps?
But when I compile this code with gcc and clang, it shows that the deduced T is const int*
My Question: Why the deduced template argument is const int* and not int* as I expect?
Why the deduced template argument is const int* and not int* as I expect?
The const in P = const T is a top level const and applies to T while the const in A = const int* is a low-level const meaning it does not apply to the pointer. This in turn means that you can't directly compare const T with const int* the way you have done.
Therefore, T will be deduced as the simplest argument type const int* and the function parameter will be of type const int *const &&.

Template conversion operator issue

I have done a bit of browsing and this was the most relevant link that I could find, however it does not answer my question
Question: Why does the template substitution fail and the following does not compile?
template <typename T>
struct A
{
A() {};
A(T value) : val(value){}
operator T() { return this->val;}
T val;
};
A<std::string> test;
std::cout << "xxx" + std::string(test); //works fine
std::cout << "xxx" + test; //compiler error
Error message:
error: no match for 'operator+' (operand types are 'const char [4]' and 'A<std::__cxx11::basic_string<char> >')
19 | std::cout << "xxx" + test;
| ~~~~~ ^ ~~~~
| | |
| | A<std::__cxx11::basic_string<char> >
| const char [4]
std::operator+(std::basic_string) is a set of operator templates, template argument deduction needs to be performed on the 2nd operand test. But implicit conversion (from A<std::string> to std::string) won't be considered in template argument deduction.
Type deduction does not consider implicit conversions (other than type adjustments listed above): that's the job for overload resolution, which happens later.
As you have showed, explicit conversion like "xxx" + std::string(test); works fine. You can also specify template arguments explicitly (in ugly way) to bypass template argument deduction.
operator+ <char, std::char_traits<char>, std::allocator<char>>("xxx", test);

enable_if compilation question void = nullptr

Does anyone know why assigning type* = 0 doesn't work, while type* = nullptr does? In both cases typedef void type. Thanks
#include <type_traits>
#include <iostream>
template <class T,
typename std::enable_if<std::is_integral<T>::value>::type* = 0>
void do_stuff(T& t) {
std::cout << "do_stuff integral\n";
}
#if 0 // works
template <class T,
typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
void do_stuff(T& t) {
std::cout << "do_stuff integral\n";
}
#endif
struct S {};
int main(int argc, char *argv[])
{
int i = 1;
do_stuff(i);
return 0;
}
Compilation:
clang++ -pedantic -Wall -std=c++11 test190.cc && ./a.out
test190.cc:23:5: error: no matching function for call to 'do_stuff'
do_stuff(i);
^~~~~~~~
test190.cc:6:6: note: candidate template ignored: substitution failure
[with T = int]: null non-type template argument must be cast to template
parameter type 'typename
std::enable_if<std::is_integral<int>::value>::type *' (aka 'void *')
void do_stuff(T& t) {
^
1 error generated.
Technically speaking, this is because a non-type template argument must be a "converted constant expression" of the parameter type. This means that the argument itself must be a constant expression, and its conversion to the required parameter type must use only the conversions specified in [expr.const]/4.
According to [expr.const]/4, null pointer conversions are only allowed from std::nullptr_t. In other words, the conversion from 0 to a null pointer value is not allowed as part of the implicit conversion sequence in a converted constant expression.
Yet it's perfectly legitimate to specify static_cast<T*>(0) as a template argument to a non-type template parameter of type T*. In other words, a null pointer conversion from 0 is allowed as part of a constant expression. It's only when the conversion is done at a certain point---after computing the argument and while converting the argument type to the parameter type---that the standard forbids it.
I have no idea about the rationale for this rule.
** nullptr and 0 are not the same. **
For a very clear explanation please see the following:
https://hackernoon.com/what-exactly-is-nullptr-in-c-94d63y6t
#brian has provided a very good technical answer, but I felt it necessary to add this answer since we should no longer be trying to use 0 for pointer values.

C++ Template specialization by type of non-type parameter

Is it valid to have variations of the same template function that differ by the type of a non-type member?
template<typename T, unsigned int V>
void f(unsigned int& v) { v = V; }
template<typename T, bool B>
void f(bool& b) { b = B; }
The intent is that one be able to call
unsigned int meaningOfLife;
f<sometype, 42>(meaningOfLife);
bool areYouAlive;
f<sometype, true>(areYouAlive);
clang and gcc are silent but MSVC reports
warning C4305: 'specialization': truncation from 'int' to 'bool'
I'd like to avoid requiring specification of the constant type:
f<sometype, bool, true>
and want to ensure that the constant value and the destination value match.
---- mcve ----
#include <iostream>
template<unsigned int V>
void f(unsigned int& v) { v = V; }
template<bool B>
void f(bool& b) { b = B; }
int main()
{
unsigned int u { 0 };
bool b { false };
f<42>(u);
f<true>(b);
std::cout << u << b;
}
Rextester example: http://rextester.com/VIGNP16100
Warning(s):
source_file.cpp(14): warning C4305: 'specialization': truncation from 'int' to 'bool'
/LIBPATH:C:\boost_1_60_0\stage\lib
Microsoft (R) C/C++ Optimizing Compiler Version 19.00.23506 for x64
421
Short answer: The code is OK and MSVC emits a bogus warning.
MSVC and g++ both have bugs in non-type template argument matching but they do select the right one for your particular example.
Long answer: It is OK to have overloaded function templates with non-type template parameters.
However the matching of a template-argument to a template declaration does not work as might be expected (by me anyway). ALL matching templates are entered into overload resolution. It does not, at any stage, prefer an "exact match".
According to C++17 [temp.arg.nontype/]2, converted constant expressions are allowed. That means, for example:
42 matches int and unsigned int.
42u matches int and unsigned int.
1u matches unsigned int, int and bool.
Note that a converted constant expression cannot contain a narrowing conversion, and int to bool is narrowing unless the value is a constant expression of value 0 or 1. So 42 does not match bool. (Ref: C++17 [expr.const]/4).
If we had the following setup:
template<unsigned int V> void g() {}
template<bool B> void g() {}
then the correct behaviour is:
g<42>() calls g<unsigned int>.
g<1>() is ambiguous.
g<1u>() is ambiguous.
MSVC 2017 and g++ 7,8 all incorrectly allow g<42> to match g<bool>, and report g<42> as ambiguous.
MSVC emits the warning that you see whilst generating the invalid match; g++ gives no diagnostic at all. If we remove the unsigned int overload then g++ silently accepts the invalid code with no diagnostic.
In your code there is a non-const lvalue reference parameter:
template<unsigned int V> void h(unsigned int&) {}
template<bool B> void h(bool&) {}
This makes a difference because overload resolution can make a selection based on the function argument. For the call:
unsigned int m;
h<1u>(m);
then both overloads of h are entered into overload resolution, however then h<unsigned int> wins because h<bool>(m) would be invalid.
As discussed above, The call h<42>(m); wins at the first stage because this cannot match h<bool>; but in MSVC++ (and g++) it incorrectly allows h<bool> to go through at this stage, but prunes it later as for the h<1u> case.

How function template type deduction works when parameter type is const lvalue reference vs non-const lvalue refernce in c++11

template<typename T>
void fun(T& param) // param is a reference
{
T abc;
abc=1;//cannot change abc value because T is deduced as const int
//It gives compilation error because T is deduced as const int
}
template<typename T>
void fun1(const T& param) // param is a reference
{
T abc;
abc=1;//can change abc value because T is deduced as int
//why T is deduced int here
}
int main()
{
int x = 2; // x is an int
const int cx = x; // cx is a const int
const int& rx = x; // rx is a reference to x as a const int
fun(x); // T is int, param's type is int&
fun(cx); // T is const int, param's type is const int&
fun(rx); // T is const int,param's type is const int&
fun1(x); // T is int, param's type is int&
fun1(cx); // T is int, param's type is const int&
fun1(rx); // T is int,param's type is const int&
return 0;
}
updated
Why const int is not deduced for T in case of fun1 template function(when function template parameter type is const lvalue reference) even const int cx and const int& rx is passed while instantiation but const int(T) type is deduced for fun(function template)?
Does T type deduction is depend on function template param(parameter) type.
When you have a template function like
template<typename T>
void fun(T & param)
if the parameter that is passed is actually const, or volatile, then in the template instantiation, T will also be "cv-qualified" appropriately.
When you put the cv-qualifier in the template function declaration, like so,
template<typename T>
void fun(const T & param)
then T is not bound as const. But it will be volatile if the parameter volatile.
It's actually very intuitive -- T is the simplest type that will make the function call work, i.e., make the expected type of the function argument match what was passed.
Similarly, if my function is
template <typename T>
void fun(T * param)
then if I pass it an int *, T will be bound as int.
This is described in great detail in the C++11 standard [temp.deduct.call](14.8.2.1), see part three about the CV-qualifiers:
[temp.deduct.call] (14.8.2.1)
(1) Template argument deduction is done by comparing each function template parameter type (call it P) with
the type of the corresponding argument of the call (call it A) as described below. ...
(2) If P is not a reference type:
(2.1) — If A is an array type, the pointer type produced by the array-to-pointer standard conversion (4.2) is
used in place of A for type deduction; otherwise,
(2.2) — If A is a function type, the pointer type produced by the function-to-pointer standard conversion (4.3)
is used in place of A for type deduction; otherwise,
(2.3) — If A is a cv-qualified type, the top level cv-qualifiers of A’s type are ignored for type deduction.
(3) If P is a cv-qualified type, the top level cv-qualifiers of P’s type are ignored for type deduction. If P is a
reference type, the type referred to by P is used for type deduction.
Since cx and rx exhibit the same behavior here, we will use only cx.
cx is const int.
case of fun:
T& matches against const int so T is deduced as const int, because const is not part of T &. The type of param is const int&.
case of fun1:
const T& matches against const int so T is deduced as int, because const already is in const T&. The type of param is const int&.
Let's have it all visually horizontally aligned by type match, maybe this is more clear:
fun param type: | T | &
receives : | const int |
fun1 param type: | const | T | &
receives : | const | int |