enable_if compilation question void = nullptr - c++

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.

Related

"Cannot form reference to void" error even with `requires(!std::is_void_v<T>)`

I'm writing a pointer class and overloading the dereference operator operator*, which returns a reference to the pointed-to object. When the pointed-to type is not void this is fine, but we cannot create a reference to void, so I'm trying to disable the operator* using a requires clause when the pointed-to type is void.
However, I'm still getting compiler errors from GCC, Clang, and MSVC for the void case even though it does not satisfy the requires clause.
Here is a minimal example and compiler explorer link (https://godbolt.org/z/xbo5v3d1E).
#include <iostream>
#include <type_traits>
template <class T>
struct MyPtr {
T* p;
T& operator*() requires(!std::is_void_v<T>)
{
return *p;
}
};
int main() {
int x = 42;
MyPtr<int> i_ptr{&x};
*i_ptr = 41;
MyPtr<void> v_ptr{&x};
std::cout << *static_cast<int*>(v_ptr.p) << '\n';
std::cout << x << '\n';
return 0;
}
And here is the error (in Clang):
<source>:7:6: error: cannot form a reference to 'void'
T& operator*()
^
<source>:20:17: note: in instantiation of template class 'MyPtr<void>' requested here
MyPtr<void> v_ptr{&x};
^
1 error generated.
ASM generation compiler returned: 1
<source>:7:6: error: cannot form a reference to 'void'
T& operator*()
^
<source>:20:17: note: in instantiation of template class 'MyPtr<void>' requested here
MyPtr<void> v_ptr{&x};
^
1 error generated.
Execution build compiler returned: 1
However, if I change the return type of operator* from T& to auto&, then it works in all 3 compilers. If I use trailing return type auto ... -> T& I also get errors in all 3 compilers.
Is this a triple compiler bug, user error, or is this intended behavior?
The requires clause doesn't matter because T is a parameter of the class template. Once T is known, the class can be instantiated, but if T is void, that instantiation fails because of the member function signature.
You can either put that requires on the entire class, or make the member function a template like this:
template<typename U = T>
U& operator*() requires(!std::is_void_v<U> && std::is_same_v<T, U>)
{
return *p;
}
Demo
Making the return type auto& is almost the same thing: the return type is deduced by replacing auto with an imaginary type template parameter U and then performing template argument deduction. Note that the version above with requires makes the compilation error clear if you try to use this function with U=void: GCC says template argument deduction/substitution failed: constraints not satisfied.
I don't think there is a way to reproduce exactly what an auto& return type does by making the function a template. Something like this might come close:
template<typename U = T>
std::enable_if_t<!std::is_void_v<T>, U>& operator*()
{
return *p;
}
Compare what you're trying with the equivalent using std::enable_if (without concepts):
template<std::enable_if_t<!std::is_void_v<T>, bool> = true>
T& operator*()
{
return *p;
}
This will give you an error like no type named 'type' in 'struct std::enable_if<false, bool>', because SFINAE wouldn't work in this situation where T is not a parameter of the function template.
Technically, you can also change the return type depending on whether T is void, but this is probably a bad idea:
using R = std::conditional_t<std::is_void_v<T>, int, T>;
R& operator*()
{
// calling this with T=void will fail to compile
// 'void*' is not a pointer-to-object type
return *p;
}
In addition to the Nelfeal's answer, let me give an alternative solution. The problem is not in the dependence of requires condition on T, but is in the return type T&. Let's use a helper type trait:
std::add_lvalue_reference_t<T> operator*()
requires(!std::is_void_v<T>)
{
...
}
It works because std::add_lvalue_reference_t<void> = void, which makes operator*() signature valid for T = void.

Why can't this parameter pack accept function pointers?

I'm trying to create a parameter pack full of function pointers, but GCC (with c++17 standard) generates a deduction failed error. Why is that?
As written here:
For pointers to functions, the valid arguments are pointers to functions with linkage (or constant expressions that evaluate to null pointer values).
In my example, that's the case (isn't it?).
Is this rule invalidated for parameter packs? Did I miss something in the standard? If that's the case, how can I fix my code, without passing the function pointers as function arguments (ie without declaring T run2(T input, Funcs... funcs).
// In f.hpp
template<typename T>
T run2(T input)
{
return input;
}
template<typename T, T(*f)(T), class ... Funcs>
T run2(T input)
{
return run2<T, Funcs...>(f(input));
}
// In m.cpp
unsigned add2(unsigned v)
{
return v+2;
}
int main()
{
unsigned a=1;
a = run2<unsigned, add2>(a); // works
a = run2<unsigned, add2, add2>(a); // doesn't work
std::cout << a << std::endl;
return 0;
}
This the error I get with run2<unsigned, add2, add2> (GCC doesn't tell me why the last attempt actually failed):
m.cpp: In function ‘int main()’:
m.cpp:37:37: error: no matching function for call to ‘run2(unsigned int&)’
a = run2<unsigned, add2, add2>(a);
^
In file included from m.cpp:2:0:
./f.hpp:85:3: note: candidate: template<class T> T run2(T)
T run2(T input)
^
./f.hpp:85:3: note: template argument deduction/substitution failed:
m.cpp:37:37: error: wrong number of template arguments (3, should be 1)
a = run2<unsigned, add2, add2>(a);
^
In file included from m.cpp:2:0:
./f.hpp:109:3: note: candidate: template<class T, T (* f)(T), class ... Funcs> T run2(T)
T run2(T input)
^
./f.hpp:109:3: note: template argument deduction/substitution failed:
You declared a type parameter pack, class... Funcs. You can't pass function pointers as arguments for type parameters, because they are values, not types. Instead, you need to declare the run2 template so that it has a function pointer template parameter pack. The syntax to do so is as follows:
template<typename T, T(*f)(T), T(*...fs)(T)>
T run2(T input)
{
return run2<T, fs...>(f(input));
}
(The rule is that the ... is part of the declarator-id and goes right before the identifier, namely fs.)
The pack fs can accept one or more function pointers of type T (*)(T).

Variadic function wont compile with clang

First off, I apologize if this is a duplicate, I will be happy to take it down but I am not even sure what the issue/diagnosis is here.
Anyway, my code here works with gcc and not clang - why is this the case? I am clearly not able to understand why clang cant compile this.
#include <iostream>
#include <type_traits>
using std::cout;
using std::endl;
template <typename T, typename... Args, typename std::enable_if<!sizeof...(Args)>::type* = nullptr>
void func(int start_size, int idx)
{
cout << start_size << " " << idx << endl;
return;
}
template <typename T, typename... Args, typename std::enable_if<sizeof...(Args)>::type* = nullptr>
void func(int start_size, int idx)
{
if((idx + 1) == int(start_size - int(sizeof...(Args))))
{
cout << start_size << " " << idx << endl;
return;
}
func<Args...>(start_size, idx);
}
template <typename... Args>
void func_wrapper(int idx)
{
func<Args...>(sizeof...(Args),idx);
}
int main()
{
func_wrapper<int,double,char>(1);
}
Error:
prog.cc:37:5: error: no matching function for call to 'func'
func<Args...>(sizeof...(Args),idx);
^~~~~~~~~~~~~
prog.cc:44:5: note: in instantiation of function template specialization 'func_wrapper<int, double, char>' requested here
func_wrapper<int,double,char>(1);
^
prog.cc:16:6: note: candidate template ignored: requirement '!sizeof...(Args)' was not satisfied [with T = int, Args = <double, char>]
void func(int start_size, int idx)
^
prog.cc:23:6: note: candidate template ignored: substitution failure [with T = int, Args = <double, char>]: non-type template argument evaluates to 2, which cannot be narrowed to type 'bool'
void func(int start_size, int idx)
^
1 error generated.
Wandbox: https://wandbox.org/permlink/yqki47uYcwUlE013
clang is correct in rejecting this code. To eliminate the error, you should explicitly apply a conversion:
std::enable_if<bool(sizeof...(Args))>
The reasoning is:
[temp.arg.nontype]/5
The following conversions are performed on each expression used as a
non-type template-argument. If a non-type template-argument cannot be
converted to the type of the corresponding template-parameter then the
program is ill-formed.
(5.1) For a non-type template-parameter of integral or enumeration
type, conversions permitted in a converted constant expression
([expr.const]) are applied.
Then [expr.const]/3 says that a narrowing conversion is not considered automatically:
A converted constant expression of type T is a literal constant
expression, implicitly converted to type T, where the implicit
conversion (if any) is permitted in a literal constant expression and
the implicit conversion sequence contains only user-defined
conversions, lvalue-to-rvalue conversions ([conv.lval]), integral
promotions ([conv.prom]), and integral conversions ([conv.integral])
other than narrowing conversions ([dcl.init.list])
Thus for this case, a narrowing conversion from std::size_t to bool, it should be explicit: bool(sizeof...(Args))
In addition of the missing explicit conversion from std::size_t to bool which would require code as
std::enable_if<sizeof...(Args) != 0>
There is an other error:
temp#res-8.3
The program is ill-formed, no diagnostic required, if:
[..]
every valid specialization of a variadic template requires an empty template parameter pack,
So
template <typename T,
typename... Args,
typename std::enable_if<sizeof...(Args) == 0>::type* = nullptr>
is invalid.
(There is also void* = nullptr which might be problematic).
You may write it with 2 overloads instead:
template <typename T>
void func(int start_size, int idx)
{
cout << start_size << " " << idx << endl;
}
template <typename T, typename T2, typename... Args> // Addition of T2, and change sizeof...
void func(int start_size, int idx)
{
if ((idx + 1) == int(start_size - int(1 + sizeof...(Args)))) {
cout << start_size << " " << idx << endl;
return;
}
func<T2, Args...>(start_size, idx);
}

How to pass array to function template with reference

I am learning c++ template concepts. I do not understand the following.
#include <iostream>
#include <typeinfo>
using namespace std;
template <typename T>
T fun(T& x)
{
cout <<" X is "<<x;
cout <<"Type id is "<<typeid(x).name()<<endl;
}
int main ( int argc, char ** argv)
{
int a[100];
fun (a);
}
What i am trying?
1) T fun (T & x)
Here x is a reference, and hence will not decayed 'a' into pointer type,
but while compiling , i am getting the following error.
error: no matching function for call to ‘fun(int [100])’
When I try non-reference, it works fine. As I understand it the array is decayed into pointer type.
C-style arrays are very basic constructs which are not assignable, copyable or referenceable in the way built-ins or user defined types are. To achieve the equivalent of passing an array by reference, you need the following syntax:
// non-const version
template <typename T, size_t N>
void fun( T (&x)[N] ) { ... }
// const version
template <typename T, size_t N>
void fun( const T (&x)[N] ) { ... }
Note that here the size of the array is also a template parameter to allow the function to work will all array sizes, since T[M] and T[N] are not the same type for different M, N. Also note that the function returns void. There is no way of returning an array by value, since the array is not copyable, as already mentioned.
The problem is in the return type: you cannot return an array because arrays are non-copiable. And by the way, you are returning nothing!
Try instead:
template <typename T>
void fun(T& x) // <--- note the void
{
cout <<" X is "<<x;
cout <<"Type id is "<<typeid(x).name()<<endl;
}
And it will work as expected.
NOTE: the original full error message (with gcc 4.8) is actually:
test.cpp: In function ‘int main(int, char**)’:
test.cpp:17:10: error: no matching function for call to ‘fun(int [100])’
fun (a);
^
test.cpp:17:10: note: candidate is:
test.cpp:7:3: note: template<class T> T fun(T&)
T fun(T& x)
^
test.cpp:7:3: note: template argument deduction/substitution failed:
test.cpp: In substitution of ‘template<class T> T fun(T&) [with T = int [100]]’:
test.cpp:17:10: required from here
test.cpp:7:3: error: function returning an array
The most relevant line is the last one.

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

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;
}