pass reference to array in C++ - c++

Can any one help me understand the following code
#include <iostream>
void foo(const char * c)
{
std::cout << "const char *" << std::endl;
}
template <size_t N>
void foo(const char (&t) [N])
{
std::cout << "array ref" << std::endl;
std::cout << sizeof(t) << std::endl;
}
int main()
{
const char t[34] = {'1'};
foo(t);
char d[34] = {'1'};
foo(d);
}
The output is
const char *
array ref
34
Why does the first foo calls the const char * version ? How can I make it call the reference version ?

Conversion of const char[N] to const char* is considered an "exact match" (to make literals easier, mainly), and between two exact matches a non-template function takes precedence.
You can use enable_if and is_array to force it to do what you want.
A messy way to force it might be:
#include <iostream>
template <typename T>
void foo(const T* c)
{
std::cout << "const T*" << std::endl;
}
template <typename T, size_t N>
void foo(const T (&t) [N])
{
std::cout << "array ref" << std::endl;
}
int main()
{
const char t[34] = {'1'};
foo(t);
char d[34] = {'1'};
foo(d);
}
/*
array ref
array ref
*/
I realise that the OP had char not some generic T, but nonetheless this demonstrates that the problem lay in one overload being a template and not the other.

Let's look at this modified example with no template.
void foo(const char * c)
{
std::cout << "const char *" << std::endl;
}
void foo(const char (&t) [34])
{
std::cout << "const char (&) [34]" << std::endl;
}
int main()
{
const char t[34] = {'1'};
foo(t);
}
My compiler says call of overloaded foo is ambiguous. This is because conversions from array to pointer are considered an "Exact" conversion sequence and are not better than the null conversion sequence for overload resolution (Standard section 13.3.3.1.1.)
In the original code, the template parameter N can be deduced as 34, but then both non-template foo(const char*) and foo<34>(const char (&)[34]) are considered in overload resolution. Since neither is better than the other by conversion rules, the non-template function beats the template function.
Fixing things seems tricky. It seems like the is_array template from header <type_traits> (from C++0x if possible or Boost if not) might help.

This appears to be different for various compilers.
Mircosoft and Borland both use the const char* version, while GNU is giving the output you described.
Here is a snippet from the C++ standard:
14.8.2.1 Deducing template arguments from a function call
[temp.deduct.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.
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;
otherwise,
-- 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,
-- If A is a cv-qualified type, the top level cv-qualifiers of A's type
are ignored for type deduction.
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
The compiler will build an A list as follows:
Argument: t d
A: char const[34] char[34]
And parameter list P:
Parameter: c t
P: char const* char const& t[N]
By default the compiler should choose non-referenced parameters. GNU is dong it wrong the second time for some reason.

Related

Rationale for overloading C-String and char pointers [duplicate]

I'm playing around with overloading operators in c++14, and I tried to match two types of arguments: any-old-const-char*, and a-string-literal.
That is, I'm trying to see if I can discriminate between:
const char * run_time;
and
"compile time"
I wrote the code below, and as shown, when I try span >> "literal" it invoked the const char* function.
When I #if 0-out the const char* version, the template version gets called just fine.
If I change the template version to take an rvalue-reference (&&) parameter for literal, it doesn't compile.
If I add a const char (&literal)[] non-template version, the const char* version is still preferred. Removing the const-char* version, the template version is preferred.
Can you explain this? In particular:
Why is const char* preferred over const char (&)[N]?
Why is const char (&)[N] preferred over const char (&)[] (non-template)?
Why is const char (&&)[N] unable to compile?
Is there a "right way" to capture literal strings?
Thanks.
#include <iostream>
using namespace std;
#include <gsl/gsl>
#include <type_name.h++>
template<unsigned N>
auto
operator>>(gsl::span<const char*,-1>& spn, const char (&literal)[N])
-> gsl::span<const char*, -1>&
{
cout << "Got array: " << literal << endl;
return spn;
}
auto
operator>>(gsl::span<const char*,-1>& spn, const char *literal)
-> gsl::span<const char*, -1>&
{
cout << "Got const-char*: " << literal << endl;
return spn;
}
#if 0
#endif
int
main(int argc, const char *argv[])
{
auto spn = gsl::span<const char*>(argv, argc);
cout << type_name<decltype(spn)>() << endl; // gsl::span<const char *, -1>
cout << type_name<decltype("literal")>() << endl; // char const (&)[8]
cout << type_name<decltype(("literal"))>() << endl; // char const (&)[8]
auto helpx = "literal";
cout << type_name<decltype(helpx)>() << endl; // const char *
spn >> "literal"; // Got const-char*: literal
return 0;
}
Edit:
In case it matters, I'm compiling with:
c++ --std=c++14 -Iinclude -c -o main.o main.c++
And c++ says:
$ c++ --version
Apple LLVM version 8.0.0 (clang-800.0.42.1)
Target: x86_64-apple-darwin16.5.0
Thread model: posix
InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin
Why is const char* preferred over const char (&)[N]?
The reason for this is rather technical. Even though the decay of a string literal from const char[N] to const char* is a conversion, it falls into the "lvalue transformation" category and is therefore considered by [over.ics.rank]/3 to be as good as no conversion at all. Since "no conversion" is required for either overload, the non-template overload wins.
Why is const char (&)[N] preferred over const char (&)[] (non-template)?
It is not possible to bind a reference to array of unknown bound to a value of type array of known bound. Instead, a reference to array of unknown bound can only be bound to values that are themselves arrays of unknown bound.
Why is const char (&&)[N] unable to compile?
A string literal is an lvalue so I'm not sure why you would expect this to work.
Is there a "right way" to capture literal strings?
You can use a helper function template that captures its argument using a forwarding reference so as to not destroy any type information (const char* versus const char[N]) then dispatch on the type using template specialization. You'll probably also want to use SFINAE to make sure it is disabled if anything other than a const char* or const char[N] is passed in. To wit,
template <bool b>
struct f_helper;
template <>
struct f_helper<true> {
void do_it(const char*) {
puts("pointer");
}
};
template <>
struct f_helper<false> {
template <std::size_t N>
void do_it(const char (&)[N]) {
printf("array of length %zd\n", N);
}
};
template <class T, class = typename std::enable_if<std::is_same<char*, std::decay_t<T>>::value ||
std::is_same<const char*, std::decay_t<T>>::value>::type>
void f(T&& s) {
f_helper<std::is_pointer<std::remove_reference_t<T>>::value>{}.do_it(s);
}
Coliru link: http://coliru.stacked-crooked.com/a/0e9681868d715e87
The overload taking a pointer is preferred because it is not a template according to
13.3.3 Best viable function [over.match.best]
Given these definitions, a viable function F1 is defined to be a better function than another viable function
F2 if for all arguments i, ICSi(F1) is not a worse conversion sequence than ICSi(F2), and then
...
(1.7)
F1 is not a function template specialization and F2 is a function template specialization
actually non-template const char (&)[] does not seem to compile at all because it is a reference to a non-bound array. It is possible to pass a pointer like this const char [], but not array.
this should fail at least for the same reason as (2)
you can provide another template taking a reference to pointer:
template< typename = void > void
foo(char const * & text)
{
::std::cout << "got ptr" << ::std::endl;
}
Note that providing another template taking a pointer won't work because both template specializations will be fine and we'll get ambiguous choice of overloaded functions.

What type is an array?

I am asking this question with reference to the following code
#include <iostream>
using namespace std;
class A {
void foo(){}
};
template <typename T>
void func(T (&a) [1]) {cout << "In array type" << endl;}
template <typename T>
void func(T (*a) [1]) {cout << "In pointer to array type " << endl;}
template <typename T>
void func(T* a) {cout << "in pointer type" << endl;}
template <typename T>
void func(T** a) {cout << "In pointer pointer type" << endl;}
template <typename T>
void func(...) {}
int foo(int a) {return 1;}
int foo1() {return 1;}
int main() {
A a[1];
func<A>(&a);
return 0;
}
What is the type of an array? From the following code I can tell that when you take the address of an array with the & operator the function call resolves to the one with T (*a) [1] and the call is not ambiguous, but the when I change the code to the following
#include <iostream>
using namespace std;
class A {
void foo(){}
};
template <typename T>
void func(T (&a) [1]) {cout << "In array type" << endl;}
template <typename T>
void func(T (*a) [1]) {cout << "In pointer to array type " << endl;}
template <typename T>
void func(T* a) {cout << "in pointer type" << endl;}
template <typename T>
void func(T** a) {cout << "In pointer pointer type" << endl;}
template <typename T>
void func(...) {}
int foo(int a) {return 1;}
int foo1() {return 1;}
int main() {
A a[1];
func<A>(a); // <-- CHANGE HERE
return 0;
}
I get an error, saying that the call to the function is ambiguous. So the imaginary type_of(a) and T* are equivalent. How then are the types of &a and T** not equivalent?
The way I am trying to explain this to myself is that the type of an array to objects is T (&) [N] and the type of an array of objects of type T is T (*) [N]. And that the standard allows implicit conversion from the first (i.e. from T (&a) [N] to T*) but when you take the address of an array it does not implicitly go from a T (*) [N] to a T**. Am I correct? Or am I inferring what T (*) [N] is wrongly?
Further how do you go about reading the meaning of syntax like this. Is there a document I can refer to?
Thanks!
... the type of an array to objects is T (&) [N] ...
No. The type of an array is T[N]. The type of a is A[1]. What you are describing is a reference to an array, and your original example involves passing a pointer to an array, which has type A(*)[1]. The other rule you are running into is that an array can decay into a pointer. In other words, an object of type T[N] can be implicitly converted to an object of type T*.
It's because of that implicit conversion that your second example fails. The relevant overloads are simply:
template <typename T> void func(T (&)[1]); // (1)
template <typename T> void func(T* ); // (2)
Both overloads are viable. The first is an exact match, whereas the second involves an array-to-pointer conversion. But according to the table in [over.ics.rank], the way to determine which viable candidate is better involves selecting conversion sequences thusly:
Standard conversion sequence S1 is a better conversion sequence than standard conversion sequence S2 if
S1 is a proper subsequence of S2 (comparing the conversion sequences in the canonical form defined by 13.3.3.1.1, excluding
any Lvalue Transformation; the identity conversion sequence is considered to be a subsequence of any non-identity conversion
sequence) or, if not that,
the rank of S1 is better than the rank of S2, or S1 and S2 have the same rank and are distinguishable by the rules in the paragraph below, or, if not that,
[..]
The array-to-pointer conversion is an lvalue transformation, so it's excluded from that bullet point. Thus, neither conversion sequence is considered better, so we have to move onto the other tiebreakers. Yet, both functions are templates, and neither is more specialized than the other. That's why it's ambiguous.

What is the exact type of "" when deduced by `auto`?

In this line:
auto a = "Hello World";
What is the exact Type of a? I'd guess char[] or const char* const but I'm not sure.
N4296 2.13.5/8
Ordinary string literals and UTF-8 string literals are also referred
to as narrow string literals. A narrow string literal has type “array
of n const char”, where n is the size of the string as defined below,
and has static storage duration (3.7).
But since variable is initialized as in your code it is actually const char*, you can check it like this.
template<typename> struct TD;
int main()
{
auto a = "Hello World";
TD<decltype(a)> _;
}
Here will be compile error in which you can see the actual type of TD instance, something like this with clang
error: implicit instantiation of undefined template 'TD<const char *>'
N4296 7.1.6.4
If the placeholder is the auto type-specifier, the deduced type is
determined using the rules for template argument deduction.
template<typename> struct TD;
template<typename T>
void f(T)
{
TD<T> _;
}
int main()
{
auto c = "Hello";
TD<decltype(c)> _;
f("Hello");
}
Both instantiated objects of type TD has type TD<const char*>.
N4926 14.8.2.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.
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
Unless you've reason to think it'd be implementation or un-defined, can just test:
#include <iostream>
template <typename T> void f() { std::cout << "other\n"; }
template <> void f<const char*>() { std::cout << "const char*\n"; }
template <> void f<const char* const>()
{ std::cout << "const char* const\n"; }
template <> void f<const char(&)[12]>() { std::cout << "const char[12]\n"; }
int main()
{
auto a = "Hello World";
f<decltype(a)>();
}
Output:
const char*
Checking that ++a compiles is another clue (it does), and while implementation defined #include <typeinfo> / typeid(a).name() can often help answer such questions.
Change to auto& a and you'll see a changes to const char(&)[12].
You can print the type of a using typeinfo
int main()
{
auto a = "Hello World";
std::cout << "type is: " << typeid(a).name() << '\n';
}
on gcc it will print
pi is: PKc
which stands for pointer to constant char
If you're in Windows the output will be a lot more readable, but you get used to this syntax too.
If you know more or less which type you a re looking for, you can also check if two types are equivalent with:
#include <typeinfo>
std::cout << std::is_same<const char*, decltype(a)>::value << std::endl;

Why does template parameter deduction for T 'skips' the constness of array elements when function parameter is const reference to T?

Let's consider those definitions:
/*** full type information with typeid ***/
template <class> class Type{};
template <class T> std::string typeStr()
{ return typeid(Type<T>).name(); }
/*** function template for parameter deduction ***/
template <class T> void func(const T &a)
{
std::cout << "Deduced type for T is: " << typeStr<T>() << std::endl;
std::cout << "\targument type is: " << typeStr<decltype(a)>() << std::endl;
}
with pointers to const
If the following statements are executed:
const int i=5, *ip=&i;
func(ip);
The output is:
Deduced type for T is: 4TypeI**PKi**E
So T is actually deduced as a pointer to a constant integer. The fact that the argument is a reference-to-const does not change the deduction, which is what one would expect because the constness of the pointer is low-level.
but with array of const
Nonetheless, if following statements are executed:
const int ia[3] = {3, 2, 1};
func(ia);
The output is:
Deduced type for T is: 4TypeI**A3_i**E
So T is actually deduced as an array of 3 non-const integers. The fact that the argument is a reference-to-const does change the deduction, as if the const was slipping into the array elements.
Actually, versions of CL up to 18 were deducing T as array of 3 const integers was what I expected to be standard, but it seems that since v19 it converged to what GCC and Clang are doing (i.e., deducing as non-const).
Thus, I assume the later behaviour to be standard, but was is the rationale ? It could seem surprising that it does not behave like with pointers.
Edit: Following dip comment, I will report here pointers to CWG issues related to this behaviour, pointers he actually posted as a comment on this answer (answer that actually raised this new question... C++ feels like a deep tunnel)
CWG 1059
CWG 1610
CWG 112
Using this function template prototype:
template <typename T> void func(const T& a);
In your first example, the type deduction works as:
const int* ip;
func(ip) => func<const int*>(const (const int*)& a)
^^^^^^^^^^ ^^^^^^^^^^
Note: This is pseudocode. The full type is const int* const&.
Note that the const int remains const int, but the * becomes * const.
This is because const int* is just a regular, mutable, non-volatile pointer. It is just a *. What it points to is irrelevant.
But in the second example, you have:
const int ia[3];
func(ia) => func<int[3]>(const (int[3])& a)
^^^^^^ ^^^^^^
Note: This is pseudocode. The real type would be const int (&a)[3].
So the type deduction is working the same in both cases, discarding the outer const.
It so happens that a const array is the same as an array of const elements.
It might help to write types like this:
template <typename T> func(T const & a);
int const * ip;
func(ip) => func<int const *>(int const * const & a)
int const ia [3];
func(ia) => func<int [3]>(int const (& a) [3])
On that second example, the const appears to "move" from being applied on the array to being applied on the elements. This is because you can't really have a const array, only an array of const elements.

how to decay array type to const pointer type in C++?

I would like to automatically generate const accessor function for given member but I struggle with arrays. It is possible to "decay" array type to a pointer, but I do not know how to make type of pointed value const? Any obvious method of adding const will only apply the pointer. Of course, I can make specialised accessor for array types, but it is not ideal solution. Returning const pointer to const value would also be acceptable. This is example of incomplete accessor:
auto foo() const -> const typename std::decay<decltype(foo_)>::type { return foo_; }
If you intend to get the address of a member array, simply qualify it as const
#include <iostream>
using namespace std;
struct fooType {
};
class MyClass {
public:
fooType foo_[2];
auto foo() const -> typename std::decay<const decltype(foo_)>::type
{ return &foo_[0]; }
};
int main() {
MyClass classObj;
classObj.foo();
return 0;
}
http://ideone.com/PjclAf
Edit:
The documentation states that
Applies lvalue-to-rvalue, array-to-pointer, and function-to-pointer
implicit conversions to the type T, removes cv-qualifiers, and defines
the resulting type as the member typedef type. This is the type
conversion applied to all function arguments when passed by value.
(emphasis mine)
The important takeaway here is that std::decay() always act to "simulate" a pass-by-value mechanism with the type you're feeding it. Cv-qualifiers are dropped iff they can be dropped in a pass-by-value call, not if they actually define the resulting type.
Take the following example:
#include <iostream>
#include <type_traits>
template <typename T, typename U>
struct decay_equiv :
std::is_same<typename std::decay<T>::type, U>::type
{};
void function1(int happyX) {
// happyX can be modified, it's just a local variable
happyX = 42;
std::cout << happyX << std::endl;
}
void function2(const int *ptrByValue) {
// ptrByValue can be modified, however its type is 'const int' and that CANNOT be modified
ptrByValue = (const int*)0xDEADBEEF;
std::cout << ptrByValue << std::endl;
}
int main()
{
std::cout << std::boolalpha
<< decay_equiv<const int, int>::value << std::endl // cv-qualifiers are dropped (pass-by-value)
<< decay_equiv<const int[2], int*>::value << std::endl; // cv-qualifiers here CANNOT be dropped, they're part of the type even if passed by value
const int myConstValue = 55;
function1(myConstValue);
const int myArrayToConstValues[2] = {4,2};
function2(myArrayToConstValues);
return 0;
}
http://ideone.com/AW6TJS
In your example you're asking for a constant return value (you can't modify the address of the first element) but asking in the trailing return type for a non-const one, that's why the compiler is complaining and what I just wrote is the reason why the const cannot be dropped by std::decay(): it is part of the type even in a pass-by-value situation (e.g. function2()).