Array decay to pointers in templates - c++

Please consider this code:
#include <iostream>
template<typename T>
void f(T x) {
std::cout << sizeof(T) << '\n';
}
int main()
{
int array[27];
f(array);
f<decltype(array)>(array);
}
Editor's Note: the original code used typeof(array), however that is a GCC extension.
This will print
8 (or 4)
108
In the first case, the array obviously decays to a pointer and T becomes int*. In the second case, T is forced to int[27].
Is the order of decay/substitution implementation defined? Is there a more elegant way to force the type to int[27]? Besides using std::vector?

Use the reference type for the parameter
template<typename T> void f(const T& x)
{
std::cout << sizeof(T);
}
in which case the array type will not decay.
Similarly, you can also prevent decay in your original version of f if you explicitly specify the template agument T as a reference-to-array type
f<int (&)[27]>(array);
In your original code sample, forcing the argument T to have the array type (i.e. non-reference array type, by using typeof or by specifying the type explicitly), will not prevent array type decay. While T itself will stand for array type (as you observed), the parameter x will still be declared as a pointer and sizeof x will still evaluate to pointer size.

The behaviour of this code is explained by C++14 [temp.deduct.call]:
Deducing template arguments from a function call
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
and then below:
If P is not a reference type:
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;
For the call f(array);, we have A = int[27]. A is an array type. So the deduced type T is int *, according to this last bullet point.
We can see from the qualifier "If P is not a reference type" that this behaviour could perhaps be avoided by making P a reference type. For the code:
template<typename T, size_t N>
void f(T (&x)[N])
the symbol P means T(&)[N], which is a reference type; and it turns out that there are no conversions applied here. T is deduced to int, with the type of x being int(&)[N].
Note that this only applies to function templates where the type is deduced from the argument. The behaviour is covered by separate parts of the specification for explicitly-provided function template parameters, and class templates.

You can also use templates like the following:
template <typename T, std::size_t N>
inline std::size_t number_of_elements(T (&ary)[N]) {
return N;
}
This little trick will cause compile errors if the function is used on a non-array type.

Depending on your use case, you can work around that using references:
template<typename T>
void f(const T& x) {
std::cout << sizeof(T);
}
char a[27];
f(a);
That prints 27, as desired.

Related

Function argument binding rules for passing an array by reference vs passing pointer

To prevent any confusion, I very much understand the difference between arrays and pointers, the concept of decay-to-pointer, and the concept of passing an array by reference in C++, etc.
My question here is specifically about the rules used by the compiler to select a function from a set of function overload candidates, when one overload takes an array reference, and the other overload takes a pointer.
For example, suppose we have:
template <class T, std::size_t N>
void foo(const T (&arr)[N])
{
std::cout << "Array-reference overload!" << std::endl;
}
template <class T>
void foo(const T* ptr)
{
std::cout << "Pointer overload!" << std::endl;
}
If we attempt to invoke function template foo() as follows:
const char arr[2] = "A";
foo(arr);
... then my expectation would be that the first overload, the one that takes an array reference, would be selected by the compiler.
However, using GCC 4.9.2, if I compile the above code, I get an error:
test.cpp:28:9: error: call of overloaded ‘foo(const char [2])’ is ambiguous
It's unclear to me why both overloads are considered equally good candidates by the compiler here, since the first overload matches the type exactly, whereas the second overload requires an extra decay-to-pointer step.
Now, I am able to get the above overload working by explicitly using type_traits as follows:
template <class T, std::size_t N>
void foo(const T (&arr)[N])
{
std::cout << "Array-reference overload!" << std::endl;
}
template <class T>
void foo(T ptr, typename std::enable_if<std::is_pointer<T>::value>::type* = 0)
{
std::cout << "Pointer overload!" << std::endl;
}
In this case, the program compiles and the overload that takes an array reference is selected. However, I don't understand why this solution should be necessary. I'd like to understand why the compiler considers a function that requires decay-to-pointer an equally likely overload candidate as the array reference, when the argument passed is very much an array.
the first overload matches the type exactly, whereas the second overload requires an extra decay-to-pointer step.
Because when checking the ranking of implicit conversion sequences in overload resolution, the array-to-pointer conversion is considered as an exact match, thus the 2nd overload has the same rank with the 1st one.
From the standard, $16.3.3.1.1 Standard conversion sequences [over.ics.scs] Table 13 — Conversions
Conversion Category Rank Subclause
No conversions required Identity Exact Match
... ...
Array-to-pointer conversion Lvalue Transformation Exact Match [conv.array]
... ...
It's worth noting that the rank of "No conversions required" (i.e. the case for the 1st overload) is "Exact Match" too.

Confusion about what is actually happening with this decltype statement

So I was taking a look through http://en.cppreference.com/w/cpp/types/result_of and saw the syntax for doing result_of of a member function and I just don't understand what is going on with that decltype.
Why do the args come after the decltype? Wouldn't they be important in figuring out the type of the member function? In my mind I imagine that instead of decltype(&C::Func)(C, char, int&) it should be decltype(&C::Func(C, char, int&)) or something of the like, but I'm having a hard time wrapping my head around it. Can anyone please explain why it is this syntax?
std::result_of takes a template argument of the form F(A...). F should be a type that is callable, such as a function type or a class type with an overloaded operator(). A... should be a sequence of argument types.
Therefore, if you have some expression e and some argument types A... and you want to know what result type you will get if you call e with arguments of types A... then you would put F = decltype(e) in std::result_of<F(A...)>, that is, std::result_of<decltype(e)(A...)>.
I'm copying the relevant code from the example that you pointed to:
#include <type_traits>
struct C {
double Func(char, int&);
};
int main()
{
// result_of can be used with a pointer to member function as follows
std::result_of<decltype(&C::Func)(C, char, int&)>::type g = 3.14;
static_assert(std::is_same<decltype(g), double>::value, "");
}
decltype(&C::Func) is the declared type of method Func of C, which is a function taking a C reference (corresponding to this), a char and an int reference.
Let us call this type T.
Then, result_of<T(...)>::type will be the type of the result of applying a function of type T to the arguments whose types you specify in the parentheses.
Therefore, in this example, result_of<decltype(&C::Func)(C, char, int&)>::type will be double.
As you are aware, the type T1(T2,T3) means a function returning a value of T1 and taking arguments of type T2 and T3. As soon as you are working with values of that type, you are bound to that interpretation.
std::result_of does not deal with any values, just with a type of the form T1(T2,T3), so it technically has the freedom to interpret the types any way it likes. And it actually does! If std::result_of would be parametrized over the type of a function, and return the return type of that function, the result (i.e. nested type member) would just be T1, but it isn't. The standard writers chose to implement a different functionality: std::result_of takes the type T1 in it's type parameter not to be the return type of the function to be determined, but the complete type of some callable thing (e.g. a function pointer), and it will return the type returned by that callable thing when passed the arguments T1 and T2.
Example time!
#include <iostream>
#include <ostream>
#include <type_traits>
#include <typeinfo>
int main(void)
{
// define the type of a function
typedef int ftype(char, long);
// (A) doesn't compile: you can't call an int
std::cout << typeid(std::result_of<int(char,long)>).name() << '\n';
// (B) doesn't compile: A the return type of a function may not be a function
std::cout << typeid(std::result_of<ftype(char,long)>::type).name() << '\n';
// (C) does compile and print the typeid name of int. On g++ this is "i"
std::cout << typeid(std::result_of<ftype*(char,long)>::type).name() << '\n';
}
Case (A) fails, as int is not callable, although the template parameter itself is well-formed. Case (B) fails, as the template parameter is not well-formed. In T1(T2,T3), T1 must not be a function type, as types describing a function returning a function are forbidden. Case (C) has a valid template parameter, in which the return type of the "function" describes a callable type, so std::result_of is applicable.
With this context in mind, the answer to your question is likely obvious. The expression decltype(&C::Func)(C, char, int&) describes a function type returning decltype(&C::Func) and taking the parameter types C, char and int &. As already discussed, the return type has to be something callable, which is the case for decltype(&C::Func), as it is the pointer-to-member-function type double (C::*)(char, int&). According to the definition of the INVOKE operation (see the page about callable), this type is callable with a parameter list (C, char, int&), so the application std::result_of to decltype(&C::Func)(C, char, int&) is valid.
The alternative you suggest: std::result_of<decltype(&C::Func(C, char, int&))> is not valid, as &C::Func(C, char, int&) is not a valid expression. If you want to constrain the type (in case there are multiple overloads of Func), you can do that using a cast, though. decltype(static_cast<double (C::*)(int, char&)>(&C::Func)) is a valid expression, returning (no surprise there) the type double (C::*)(int, char&). But like in example (A), this is not a type you may apply std::result_of on.
The really interesting use-case for std::result_of is the case in which T1, the callable type, is a function object. By passing the type of a function object to std::result_of as T1, you are passing all function-call operators of that object at the same time and you can have std::result_of pick the right one using overload resolution. Passing "all the Func functions" like you pass "all the operator() functions" is not possible, because std::result_of is hardwired to look for operator() in case of objects, and you can't use the address-of operator to map operator() invocations to Func() invocations. You can write a template doing this mapping, though:
#include <iostream>
#include <ostream>
#include <type_traits>
#include <typeinfo>
class S {
public:
int Func(int);
double Func(float, float);
};
template <typename T>
class call_Func : public T {
public:
template<typename... args>
auto operator()(args... vals) -> decltype(this->Func(vals...)) { return this->Func(vals...); }
};
int main(void)
{
std::cout << typeid(std::result_of<call_Func<S>(int)>::type).name() << '\n';
}
The template call_Func redirects calling operator() on call_Func<S> to calling Func on the base class S (one should use std::forward, though), but note you can not write a "generic redirector" that gets the name of the function to redirect the function call operator to as template parameter, as you can neither pass overload set nor names as template parameters, but just types and constant values (for non-type parameters). Pointer-to-member-functions are one kind of constant value, but you already lost the overloading as soon as you form such a pointer.

How does template converting operator to pointer to member function syntax work

I was looking at the emulated version of nullptr and saw this converting operator (a member of nullptr_t):
template<class C, class T> // or any type of null
operator T C::*() const // member pointer...
{ return 0; }
This syntax for pointer to member function confuses me. I usually expect to see such a type as something like
R (C::*)(I1, I2, ...)
With the template above, there's no input arguments. I can't figure out how the type deduction works in this case. I'm having trouble forming a specific question, other than, how does this work? If I have code like this:
typedef int (MyClass::*MyTypedef)(float);
MyTypedef m = nullptr;
I'm guessing T deduces to int, and C deduces to MyClass. What "happens" to float?
That is a pointer to member, not necessarily a pointer to member function. The difference is that it can generate a pointer to member function or a pointer to non-function member.
Now in the particular use case, the destination is a pointer to member, the compiler is seeing an expression in which it needs a int (MyClass::*)(float), and on the other hand it has a nullptr. It tries to find a conversion and it finds the operator T C::*(), which is a valid conversion if C is deduced to be MyClass and T is deduced to be int (float) [function taking a float and returning an int].
I also find this particular corner of the language a bit confusing (having typedefs, or deduced types for functions), for example this is legal if weird:
typedef void int_f(int);
struct X {
int_f m;
};
void X::m(int x) { std::cout << x << '\n'; }
The same thing is going on in the conversion operator that you are concerned with.

Using an int as a template parameter that is not known until run-time

I am trying to use an integer as a template parameter for a class. Here is a sample of the code:
template< int array_qty >
class sample_class {
public:
std::array< std::string, array_qty > sample_array;
}
If I do so something like this, it works:
sample_class< 10 > sample_class_instance;
However, let's say that I do not know the value of array_qty (the template parameter) when compiling, and will only know it during run-time. In this case, I would essentially be passing an int variable as the template argument. For the sake of demonstration, the following code does not work:
int test_var = 2;
int another_test_var = 5;
int test_array_qty = test_var * another_test_var;
sample_class< test_array_qty > sample_class_instance;
I get the following error during compile time when trying the above:
the value of ‘test_array_qty’ is not usable in a constant expression
I've tried converting test_array_qty to a const while passing it as the template parameter, but that doesn't seem to do the trick either. Is there any way to do this, or am I misusing template parameters? Perhaps they need to be known at compile time?
The goal is NOT to solve this specific approach, but rather to find a way to set the length of the array to an int variable that can be stated when instantiating the class. If there is a way to do this via a template parameter, that would be ideal.
Please note that I have to use an array for this, and NOT a vector which I may end up as a suggestion. Additionally, array_qty will always be a value between 0 and 50 - in case that makes a difference.
This can be done in effect. But trust me when I say you are asking the wrong question. So what follows answers your question, even thought doing it is a bad idea almost always.
What you in effect can do is create 50 different programs, one for each of the 50 possible sizes, and then conditionally jump to the one you want.
template<int n>
struct prog {
void run() {
// ...
}
};
template<int n>
struct switcher {
void run(int v) {
if(v==n)
prog<n>::run();
else
switcher<n-1>::run(v);
}
};
template<>
struct switcher<-1> {
void run(int v){
}
};
Call switcher<50>::run( value ); and if value is 0 to 50, prog<value>::run() is invoked. Within prog::run the template parameter is a compile time value.
Horrid hack, and odds are you would be better off using another solution, but it is what you asked for.
Here is a C++14 table-based version:
template<size_t N>
using index_t = std::integral_constant<size_t, N>; // C++14
template<size_t M>
struct magic_switch_t {
template<class F, class...Args>
using R=std::result_of_t<F(index_t<0>, Args...)>;
template<class F, class...Args>
R<F, Args...> operator()(F&& f, size_t i, Args&&...args)const{
if (i >= M)
throw i; // make a better way to return an error
return invoke(std::make_index_sequence<M>{}, std::forward<F>(f), i, std::forward<Args>(args)...);
}
private:
template<size_t...Is, class F, class...Args>
R<F, Args...> invoke(std::index_sequence<Is...>, F&&f, size_t i, Args&&...args)const {
using pF=decltype(std::addressof(f));
using call_func = R<F, Args...>(*)(pF pf, Args&&...args);
static const call_func table[M]={
[](pF pf, Args&&...args)->R<F, Args...>{
return std::forward<F>(*pf)(index_t<Is>{}, std::forward<Args>(args)...);
}...
};
return table[i](std::addressof(f), std::forward<Args>(args)...);
}
};
magic_switch_t<N>{}( f, 3, blah1, blah2, etc ) will invoke f(index_t<3>{}, blah1, blah2, etc).
Some C++14 compilers will choke on the variardic pack expansion containing a lambda. It isn't essential, you can do a workaround, but the workaround is ugly.
The C++14 features are all optional: you can implement it all in C++11, but again, ugly.
The f passed basically should be a function object (either a lambda taking auto as the first argument, or a manual one). Passing a function name directly won't work well, because the above best works when the first argument becomes a compile-time value.
You can wrap a function template with a lambda or a function object to help.
For C++ 11, non-type template arguments are restricted to the following (§14.3.2/1):
A template-argument for a non-type, non-template template-parameter shall be one of:
for a non-type template-parameter of integral or enumeration type, a converted constant expression (5.19) of the type of the template-parameter; or
the name of a non-type template-parameter; or
a constant expression (5.19) that designates the address of an object with static storage duration and external or internal linkage or a function with external or internal linkage, including function templates and function template-ids but excluding non-static class members, expressed (ignoring parentheses) as & id-expression, except that the & may be omitted if the name refers to a function or array and shall be omitted if the corresponding template-parameter is a reference; or
a constant expression that evaluates to a null pointer value (4.10); or
a constant expression that evaluates to a null member pointer value (4.11); or
a pointer to member expressed as described in 5.3.1.
In C++ 98 and 03, the list is even more restricted. Bottom line: what you're trying to do simply isn't allowed.
Template arguments must be compile-time constants aka "constant expressions" or constexprs for short. So there is no way to do is using templates.
You could use a dynamic-sized array and store its size in an int.
Or simply use a vector. Be sure to initialize its size in the constructor by passing the desired size to the vector's constructor!
Sorry, this is not possible. The template argument must be a constant expression known at compile time.
I'm a little late, but here's my suggestion. I'm guessing the main problem with vectors for you is that they allocate a larger capacity than what you need in order to support dynamic growth. So, can't you write your own simple array class?
template <typename T>
class Array {
private:
T* data;
unsigned size;
public:
Array(unsigned size) {
data = new T[size];
this->size = size;
}
T& operator[](int i) {
return data[i];
}
T operator[](int i) const {
return data[i];
}
// Depending on your needs, maybe add copy constructor and assignment operator here.
...
unsigned size() {
return size;
}
~Array() {
delete [] data;
}
}
From what I know, I believe this should be just as fast as the STL array class. In addition, you can create Arrays with sizes unknown until run-time, the Array's memory is handled automatically when it is destroyed, and you don't have to instantiate a new class every time you create a new array with a different size (like you have to do for STL arrays).

Argument type deduction, references and rvalues

Consider the situation where a function template needs to forward an argument while keeping it's lvalue-ness in case it's a non-const lvalue, but is itself agnostic to what the argument actually is, as in:
template <typename T>
void target(T&) {
cout << "non-const lvalue";
}
template <typename T>
void target(const T&) {
cout << "const lvalue or rvalue";
}
template <typename T>
void forward(T& x) {
target(x);
}
When x is an rvalue, instead of T being deduced to a constant type, it gives an error:
int x = 0;
const int y = 0;
forward(x); // T = int
forward(y); // T = const int
forward(0); // Hopefully, T = const int, but actually an error
forward<const int>(0); // Works, T = const int
It seems that for forward to handle rvalues (without calling for explicit template arguments) there needs to be an forward(const T&) overload, even though it's body would be an exact duplicate.
Is there any way to avoid this duplication?
This is a known problem and the purpose of rvalue references in C++0x. The problem has no generic solution in C++03.
There is some archaic historical reason why this occurs that is very pointless. I remember asking once and the answer depressed me greatly.
In general, this nasty problem with templates ought to require duplication because the semantics where a variable is const or not, or a reference or not, are quite distinct.
The C++11 solution to this is "decltype" but it is a bad idea because all it does is compound and already broken type system.
No matter what the Standard or the Committee says, "const int" is not and will never be a type. Nor will "int&" ever be a type. Therefore a type parameter in a template should never be allowed to bind to such non-types, and thankfully for deduction this is the case. Unfortunately you can still explicitly force this unprincipled substitution.
There are some idiotic rules which try to "fix" this problem, such as "const const int" reducing to "const int", I'm not even sure what happens if you get "int & &": remember even the Standard doesn't count "int&" as a type, there is a "int lvalue" type but that's distinct:
int x; // type is lvalue int
int &y = x; // type is lvalue int
The right solution to this problem is actually quite simple: everything is a mutable object. Throw out "const" (it isn't that useful) and throw away references, lvalues and rvalues. It's quite clear that all class types are addressable, rvalue or not (the "this" pointer is the address). There was a vain attempt by the committee to prohibit assigning to and addressing rvalues.. the addressing case works but is easily escaped with a trivial cast. The assignment case doesn't work at all (because assignment is a member function and rvalues are non-const, you can always assign to a class typed rvalue).
Anyhow the template meta-programming crowd have "decltype" and with that you can find the encoding of a declaration including any "const" and "&" bits and then you can decompose that encoding using various library operators. This couldn't be done before because that information is not actually type information ("ref" is actually storage allocation information).
When x is an rvalue
But x is never an rvalue, because names are lvalues.
Is there any way to avoid this duplication?
There is a way in C++0x:
#include <utility>
template <typename T>
void forward(T&& x) // note the two ampersands
{
target(std::forward<T>(x));
}
Thanks to reference collapsing rules, the expression std::forward<T>(x) is of the same value-category as the argument to your own forward function.
Assuming there are k arguments, as I understand it the only "solution" in C++03 is to manually write out 2^k forwarding functions taking every possible combination of & and const& parameters. For illustration, imagine that target() actually took 2 parameters. You would then need:
template <typename T>
void forward2(T& x, T& y) {
target(x, y);
}
template <typename T>
void forward2(T& x, T const& y) {
target(x, y);
}
template <typename T>
void forward2(T const& x, T& y) {
target(x, y);
}
template <typename T>
void forward2(T const& x, T const& y) {
target(x, y);
}
Obviously this gets very unwieldy for large k, hence rvalue references in C++0x as other answers have mentioned.