Array trait causes template argument deduction failure - c++

The following code does not compile with G++ (although I believe it should):
#include <iostream>
template <unsigned N>
struct foo_traits {
typedef const char ArrayArg[N];
typedef int Function (ArrayArg *);
};
template <unsigned N>
int foo (typename foo_traits<N>::Function *ptr) {
return ptr(&"good");
}
int bar (const char (*x)[5]) {
std::cout << *x << "\n";
return 0;
}
int main ()
{
return foo(bar);
}
I checked this with GCC 4.4 through 4.7, and I get a template argument deduction failure. With 4.7.1:
prog.cpp: In function ‘int main()’:
prog.cpp:21:19: error: no matching function for call to ‘foo(int (&)(const char (*)[5]))’
prog.cpp:21:19: note: candidate is:
prog.cpp:10:5: note: template<unsigned int N> int foo(typename foo_traits<N>::Function*)
prog.cpp:10:5: note: template argument deduction/substitution failed:
prog.cpp:21:19: note: couldn't deduce template parameter ‘N’
If I use an explicit template argument (i.e., foo<5>(bar)), it compiles fine. If I use a version of the code without the typedefs, it compiles fine:
#include <iostream>
template <unsigned N>
int fixfoo (int (*ptr) (const char (*)[N])) {
return ptr(&"good");
}
int bar (const char (*x)[5]) {
std::cout << *x << "\n";
return 0;
}
int main ()
{
return fixfoo(bar);
}
Is the failing code supposed to compile (i.e., did I make a silly mistake)?

int foo(typename foo_traits<N>::Function *ptr);
The signature makes it a non-deductible context, so you must include the template arguments so that the value N is known and so consequentially the type of the pointer ptr be known as well.
Your second example compiles because the type of the signature through bar can be deduced.

Related

Why this code snippet works with C++17 whereas the compiler complains when using C++11?

Why this code snippet works with C++17 whereas the compiler complains when using C++11(i.e https://godbolt.org/z/71G91P)?
Are there any potential problems with this code snippet?
#include<iostream>
class ctx
{
public:
int map_create(void*){std::cout << "haha" << std::endl; return 0;};
};
ctx obj;
typedef int (ctx::*ctx_mem_func)(void*);
template <ctx_mem_func func>
int regHelper(void*)
{
((&obj)->*func)(nullptr);
return 0;
}
constexpr ctx_mem_func testFunc = &ctx::map_create;
typedef int(*callBackFunc)(void*);
int reg(callBackFunc)
{
return 0;
}
int main()
{
reg(regHelper<testFunc>);
//But this expression is ok.
reg(regHelper<&ctx::map_create>);
std::cout << "this is a test" << std::endl;
}
Here are the error messages when using c++11(gun 10.0.2):
<source>: In function 'int main()':
<source>:30:28: error: no matches converting function 'regHelper' to type 'callBackFunc {aka int (*)(void*)}'
reg(regHelper<testFunc>);
^
<source>:13:5: note: candidate is: template<int (ctx::* func)(void*)> int regHelper(void*)
int regHelper(void*)
^
This is a difference between C++14 and C++17. Simplified:
int f();
template<int (&)()> struct S {};
constexpr auto& q = f;
using R = S<q>; // valid in C++17, invalid in C++14
The change is to Allow constant evaluation for all non-type template arguments, meaning that now a constexpr variable naming a function (member function, etc.) is permissible as an NTTP where previously only the actual name of the function was permitted.

GCC template argument deduction/substitution failed

The code below compiles on MSVC but fails on GCC (4.6.3). Why does it fail and what should I do to fix it?
#include <array>
class Foo {
public:
template<typename T, int N>
operator std::array<T, N>() const {
return std::array<T, N>();
}
};
int main(){
Foo val;
// both of the following lines fail on GCC with error:
// "no matching function call...", ultimately with a note:
// "template argument deduction/substitution failed"
auto a = val.operator std::array<int, 2>();
static_cast<std::array<int, 2>>(val);
return 0;
}
EDIT: The following code, however, does compile (on both compilers), despite passing in an int for std::array's template parameter.
template<int N, typename T>
struct Bar {
std::array<T, N> buf;
};
int main()
{
auto x = Bar<3, double>();
return 0;
}
If you read the full text of the error messages you get, the compiler is complaining because the type for N in your template class is int, while the second parameter of std::array is std::size_t, which is an unsigned long on your system.
Changing your template's declaration to use std::size_t N will fix the problem.
MSVC is not complaining possibly because it recognizes that the value "2" works for either case, or because of a compiler bug.

auto-returning functions and template instantiation

While writing some template code, I ran into <unresolved overloaded function type> errors which can be reduced to the following.
template <int N>
auto bar()
{
return N;
}
int main(int, char* [])
{
auto foo = [] (auto func) {
return func();
};
foo(bar<3>);
}
With the errors being:
unresolved_overload.cpp: In function 'int main(int, char**)':
unresolved_overload.cpp:26:28: error: no match for call to '(main(int, char**)::<lambda(auto:1)>) (<unresolved overloaded function type>)'
std::cout << foo(bar<3>) << std::endl;
^
unresolved_overload.cpp:21:29: note: candidate: template<class auto:1> constexpr main(int, char**)::<lambda(auto:1)>::operator decltype (((const main(int, char**)::<lambda(auto:1)>*)((const main(int, char**)::<lambda(auto:1)>* const)0))->operator()(static_cast<auto:1&&>(<anonymous>))) (*)(auto:1)() const
auto foo = [] (auto func) {
^
unresolved_overload.cpp:21:29: note: template argument deduction/substitution failed:
unresolved_overload.cpp:26:28: note: couldn't deduce template parameter 'auto:1'
std::cout << foo(bar<3>) << std::endl;
^
unresolved_overload.cpp:21:29: note: candidate: template<class auto:1> main(int, char**)::<lambda(auto:1)>
auto foo = [] (auto func) {
^
unresolved_overload.cpp:21:29: note: template argument deduction/substitution failed:
unresolved_overload.cpp:26:28: note: couldn't deduce template parameter 'auto:1'
std::cout << foo(bar<3>) << std::endl;
If we replace the auto-return with the explicit return type, int, the example will compile fine.
Why does auto-return run into these issues? I looked into template argument deduction and substitution but the search was largely unfruitful. I thought it might have something to do with the order of template instantiation / etc but couldn't make too much sense of it...
Per AndyG's suggestion, I found the same issue on GCC's bug list. Bug 64194. First reported in 2014. Thus the conclusion seems to be that this is a GCC bug and thankfully not another special case for templates.
Working around this just requires having something else to trigger the instantiation (e.g. assign to a variable, a using declaration).
Try this:
template <typename func>
auto bar(func&& f)->decltype(f())
{
return f();
}
int main()
{
int i = 100;
auto f = [=]()
{
return i;
};
bar(f);
return 0;
}

Passing array to function in C++

Why does the following not compile
#include <iostream>
template <typename T, size_t N>
const size_t len(T[N]){
return N;
}
int main(int argc, char* argv[]) {
using namespace std;
int arr[] = {1, 2, 3};
cout << len(arr);
}
but this does:
#include <iostream>
const size_t foo(int[3]) {
return 42;
}
int main(int argc, char* argv[]) {
using namespace std;
int arr[1] = {123};
cout << foo(arr);
}
but with obviously incorrect argument and strangely only with the parameter identifier omitted
I am using GCC 4.9.2 with -std=c++1y
edit:
the error message for the first example:
main.cpp:12:24: error: no matching function for call to 'len(int [3])'
cout << len(arr);
^
main.cpp:12:24: note: candidate is:
main.cpp:4:18: note: template<class T, unsigned int N> const size_t len(T*)
const size_t len(T[N]){
^
main.cpp:4:18: note: template argument deduction/substitution failed:
main.cpp:12:24: note: couldn't deduce template parameter 'N'
cout << len(arr);
^
A function argument that looks like an array is actually a pointer, for bizarre historical reasons. If you specify an array size, then it's ignored. So the template is equivalent to
template <typename T, size_t N>
const size_t len(T*){
return N;
}
The argument can be pointer or anything convertible to one, including an array of any size, so N cannot be deduced from the argument. You can fix this by taking the array by reference:
template <typename T, size_t N>
size_t len(T(&)[N]){
return N;
}
Now the argument can only be an array of known size, and N will be deduced from that size.

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.