template<int N> std::ostream& operator << (...) [duplicate] - c++

This question already has answers here:
Printing an std::array
(1 answer)
Deducing function template parameters from template argument types
(1 answer)
C++ template parameter deduction for std::array with non size_t integer
(2 answers)
Closed last month.
Why this application doesn't compile?
#include <iostream>
#include <array>
template<int N>
std::ostream& operator << (std::ostream& out, std::array<int, N> const& arr) {
for(auto& a:arr) std::cout << a << ' ';
return out;
}
int main(int argc, char const* argv[]) {
std::array<int, 10> arr {1,2,3,4,5,6,7,8,9,10};
std::cout << arr << '\n';
return 0;
}
Why it cannot resolve N? The error message is
main.cpp:13:15: error: invalid operands to binary expression ('std::ostream' (aka 'basic_ostream<char>') and 'std::array<int, 10>')
std::cout << arr << '\n';

Because the type of the 2nd template parameter of std::array is std::size_t, not int. Type mismatch causes template argument duduction failing.
If a non-type template parameter of function template is used in the template parameter list of function parameter (which is also a template), and the corresponding template argument is deduced, the type of the deduced template argument ( as specified in its enclosing template parameter list, meaning references are preserved) must match the type of the non-type template parameter exactly, except that cv-qualifiers are dropped, and except where the template argument is deduced from an array bound—in that case any integral type is allowed, even bool though it would always become true:
You can change the operator template to:
template<std::size_t N>
std::ostream& operator << (std::ostream& out, std::array<int, N> const& arr) {
for(auto& a:arr) std::cout << a << ' ';
return out;
}

I tried the code in VS 2022 using C++'s latest std and the Microsoft compiler compiles the int N version without problems haha. It seems like it does a sort of conversion. In any case, it could deal with the mismatch.
But yes, std::size_t is what you should be using instead, as #songyuanyao already stated.

Related

What can be used in the simple-template-id of a class template deduction guide?

Simple example first, doesn't compile with GCC12:
#include <array>
#include <iostream>
#include <initializer_list>
template <int nvars> class Test1 {
public:
Test1( std::initializer_list<int> IL ) : arr { 0 }
{
// See https://stackoverflow.com/questions/38932089/can-i-initialize-an-array-using-the-stdinitializer-list-instead-of-brace-enclo
};
const std::array<int,nvars> arr;
};
// CTAD guide / simple-template-id
Test1(std::initializer_list<int> IL) -> Test1<IL.size()>;
int main() {
Test1 test1({1,2,3,4});
std::cout << "nvars: " << test1.arr.size() << std::endl;
}
The simple-template_id here is Test1<IL.size()>, where IL.size() is a constexpr function returning 4. It does compile with a hardcoded 4, but that obviously defeats the purpose.
What is and is not allowed in the simple-template_id of the CTAD guide? Which names, which types of expressions?
The problem is that parameters like IL are not constexpr. This means that we can't use IL.size() as a template argument since template arguments are required to be compile time constant.
Thus to solve this we have to pass a compile time constant as a template argument. One way of doing this would be to make the parameter as a reference to an array of size N with elements of type T where both T and N are template parameters.
template<typename T, std::size_t N> Test1(T const(&&)[N]) -> Test1<N>;

Why doesn't template operator<< deduce std::endl? [duplicate]

This question already has answers here:
std::endl is of unknown type when overloading operator<<
(6 answers)
Closed 3 years ago.
This will compile and run if you uncomment the first operator definition:
#include <iostream>
struct logger
{
std::ostream &loggingStream;
logger(std::ostream &ls) : loggingStream(ls) {}
};
/*
logger &operator<<(logger &l, std::ostream & (*manip)(std::ostream &)) {
manip(l.loggingStream);
return l;
}
*/
template<typename T>
logger &operator<<(logger &l, const T &t) {
l.loggingStream << t;
return l;
}
int main() {
logger l(std::cout);
l << "Hello" << std::endl;
return 0;
}
With the comment in place:
error: no match for ‘operator<<’ (operand types are ‘logger’ and ‘<unresolved overloaded function type>’)
Why do I need to provide a non-template overload to handle endl?
std::endl is itself a template. When you have the first overload, its arguments can be deduced by matching to the function pointer. For that is one instance where TAD happens.
With just the operator<< template, what is there to deduce from? Both templates need their arguments deduced.
Because, as a function template, std::endl is an overload set with regard to template argument deduction; and template argument deduction can't work on overload sets (unless it only contains one function of course).
To illustrate, consider:
template<class Function>
void functor(Function f)
{ f(0); }
void g(float) {}
void g(double) {}
functor(g);
There is no reason to favor one version of g over the other, and unless you explicitly specialize functor (functor<void(float)>(f) is fine), template argument deduction must fail.
This is also true if g is a template: http://coliru.stacked-crooked.com/a/8e27a45bbeedd979

Template specialization unmatched error

Re-edited:
Here's what C++ Primer 5th says:
Version 1:
template <typename T> int compare(const T&, const T&);
Version 2:
template<size_t N, size_t M> int compare(const char (&)[N], const char (&)[M]);
A specialization of Version 1:
template <> int compare(const char* const &p1, const char* const &p2);
For example, we have defined two versions of our compare function template, one that takes references to array parameters and the other that takes const T&. The fact that we also have a specialization for character pointers has no impact on function matching. When we call compare on a string literal: compare("hi", "mom")
both function templates are viable and provide an equally good (i.e., exact) match to the call. However, the version with character array parameters is more specialized (§ 16.3, p. 695) and is chosen for this call.
The book says "both provide an equally good match", so then I thought putting Version 1 and its specialization should compile well. But it didn't.
So "provide an equally good match" doesn't mean it can compile? The book plays a trick on me?
Original code snippet link that I didin't understand why can't compile:
https://wandbox.org/permlink/oSCDWad03nELC9xs
Full context screenshot (I've boxed the most related part, sorry to post such a big pic here).
C-style strings are not pointers, they are arrays. When template type deduction happens, it deduces T as either const char[3] or const char[4]. Since those conflict the compiler is unable to deduce T and it stops there.
template<>
int compare(const char* const &p1, const char* const&p2) {
cout << "const char* const" << endl;
return 3;
}
won't be called because it relies on T being deduced and matching const char* and the compiler was not able to deduce T. A specialization is not a overload, it is a recipe for that specific T. If T can't be deduced then the specialization, even it it were to be a valid overload, won't be called.
If you were to overload the function instead of providing a specialization then it would compile with:
int compare(const char* const &p1, const char* const&p2) {
cout << "const char* const" << endl;
return 3;
}
You are passing to the template function two parameters of different types (the type of "hi" is const char [3] and the type of "mom" is const char [4]), so the compiler is not able to find a T that matches both types.
It's the same error that you would obtain calling std::min(0, 1U); std::min() (one of its overload) expects two arguments of the same type, as your compare() function does.
A possible solution to your problem is to accept parameters of different types:
template <typename T1, typename T2>
int compare(const T1&, const T2&);
This will work without editing the body of your function.
The compiler is unable to match it to one of the existing templates. If you read the section 16.5 carefully you will understand that it would call the second version of the template class.
The function call has 2 different types of parameters const char[3] and const char [4] compiler is unable to find a template specialization that takes 2 different data types as parameters.
The code below is one of the solutions.
#include <iostream>
#include <string>
using namespace std;
template <typename T> int compare(const T&, const T&) {
cout << "const T" << endl;
return 3;
}
template<size_t N, size_t M>
int compare(const char (&p)[N], const char (&q)[M]) {
cout<<p<<" "<<q<<endl;
return 3;
}
int main()
{
compare("hi", "mom");
}
The other solution is as below. It takes 2 different types and access the variables.
#include <iostream>
#include <string>
using namespace std;
template <typename T> int compare(const T&, const T&) {
cout << "const T" << endl;
return 3;
}
template <typename T1, typename T2>
int compare(const T1&p, const T2&q){
cout<<p<<" "<<q<<endl;
return 3;
}
int main()
{
compare("hi", "mom");
}

static assert that template typename T is NOT complete? [duplicate]

This question already has answers here:
How to write `is_complete` template?
(8 answers)
Closed 1 year ago.
Is there a way to static_assert that a type T is Not complete at that point in a header? The idea is to have a compile error if someone adds #includes down the road in places they should not be.
related: How to write `is_complete` template?
Using that link's answer,
namespace
{
template<class T, int discriminator>
struct is_complete {
static T & getT();
static char (& pass(T))[2];
static char pass(...);
static const bool value = sizeof(pass(getT()))==2;
};
}
#define IS_COMPLETE(X) is_complete<X,__COUNTER__>::value
class GType;
static_assert(!IS_COMPLETE(GType),"no cheating!");
unfortunately this gives "invalid use of incomlete type" error, d'oh. Is there a way to assert on the negation?
Here is a function using expression SFINAE based on chris proposal which allows checking whether a type is complete yet.
My adoption needs no includes, errors-out when the required argument is missing (hiding the argument was not possible) and is suitable for C++11 onward.
template<typename T>
constexpr auto is_complete(int=0) -> decltype(!sizeof(T)) {
return true;
}
template<typename T>
constexpr bool is_complete(...) {return false;}
And a test-suite:
struct S;
bool xyz() {return is_complete<S>(0);}
struct S{};
#include <iostream>
int main() {
std::cout << is_complete<int>(0) << '\n';
std::cout << xyz() << '\n';
std::cout << is_complete<S>(0);
}
Output:
1
0
1
See live on coliru
Passing a reference through ... doesn't work.
5.2.2/7:
When there is no parameter for a given argument, the argument is passed in such a way that the receiving
function can obtain the value of the argument by invoking va_arg (18.10). [note skipped — n.m.] The lvalue-to-rvalue (4.1), array-to-pointer (4.2), and
function-to-pointer (4.3) standard conversions are performed on the argument expression. An argument that
has (possibly cv-qualified) type std::nullptr_t is converted to type void* (4.10). After these conversions,
if the argument does not have arithmetic, enumeration, pointer, pointer to member, or class type, the
program is ill-formed.
Here's a kind-of-working solution hastily adapted from #chris's comment:
#include <iostream>
#include <utility>
namespace
{
template<typename T, int>
constexpr auto is_complete(int) -> decltype(sizeof(T),bool{}) {
return true;
}
template<typename T, int>
constexpr auto is_complete(...) -> bool {
return false;
}
}
#define IS_COMPLETE(T) is_complete<T,__LINE__>(0) // or use __COUNTER__ if supported
struct S;
static_assert(IS_COMPLETE(int), "oops 1!");
static_assert(!IS_COMPLETE(S), "oops 2!");
struct S {};
static_assert(IS_COMPLETE(S), "oops 3!");

pass reference to array in 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.