The following fails to deduce template arguments in VS2013 with the following error,error C2440: 'initializing' : cannot convert from 'overloaded-function' to 'f'. However compiles fine GCC.
template <class TRAIT, class ...T>
void myVarFunc(T...arg)
{
printf("test");
}
typedef void(*f)(int a, char b);
int main(int argc, char* argv[])
{
f myf = &myVarFunc<int>;
myf(1, 'x');
return 0;
}
Can anyone determine why this fails, the same function can be instantiated using the following:
myVarFunc<int>(1, 'x'); //compiles ok
And the following also compiles:
template <class ...Args>
void myVarFunc(Args...arg)
{
printf("test");
}
typedef void(*f)(int a, char b);
int main(int argc, char* argv[])
{
f myf = myVarFunc; //compiles ok
myf(1, 'x');
return 0;
}
So when adding an extra type to the template, it fails to deduce the parameters, could this be a compiler bug, or just not supported by the language?
Thanks
Related
This question already has answers here:
Where and why do I have to put the "template" and "typename" keywords?
(8 answers)
Closed 6 months ago.
I am writing a template function where one of the template parameters is a type with a member function that is itself a template function. When I invoke the template member function and explicitly specify the template parameters, it appears that the code does not compile. This is illustrated in the following minimal example:
This version will compile and run just fine:
#include <iostream>
struct ar_t
{
int data[2];
ar_t(void) {data[0] = 10; data[1] = 17;}
template <const std::size_t idx> int get(void) const {return data[idx];}
};
template <const std::size_t val> struct idx_t {};
template <const std::size_t val> int idx_ar1(const ar_t& ar, const idx_t<val>& idx)
{
return ar.get<val>();
}
int main(int argc, char** argv)
{
ar_t x;
const std::size_t index = 1;
idx_t<index> i;
idx_ar1(x,i);
return 0;
}
whereas this version will not:
#include <iostream>
struct ar_t
{
int data[2];
ar_t(void) {data[0] = 10; data[1] = 17;}
template <const std::size_t idx> int get(void) const {return data[idx];}
};
template <const std::size_t val> struct idx_t {};
template <typename arr_type, const std::size_t val> int idx_ar1(const arr_type& ar, const idx_t<val>& idx)
{
return ar.get<val>();
}
int main(int argc, char** argv)
{
ar_t x;
const std::size_t index = 1;
idx_t<index> i;
idx_ar1(x,i);
return 0;
}
Note the difference in the template parameters for idx_ar1. The error message I get with g++ 11.1 and -std=c++20 is:
main.cc: In function ‘int idx_ar1(const arr_type&, const idx_t<val>&)’:
main.cc:14:24: error: expected primary-expression before ‘)’ token
14 | return ar.get<val>();
| ^
main.cc: In instantiation of ‘int idx_ar1(const arr_type&, const idx_t<val>&) [with arr_type = ar_t; long unsigned int val = 1]’:
main.cc:22:12: required from here
main.cc:14:18: error: invalid operands of types ‘<unresolved overloaded function type>’ and ‘long unsigned int’ to binary ‘operator<’
14 | return ar.get<val>();
|
How can I get around this? I require preciesly the behaviour used in the second example. This appears to be a bug in parsing the syntax, or I don't quite have a detailed understanding of the way the member function is being declared.
Try compiling with Clang, too - sometimes it gives better errors than GCC (sometimes worse):
":14:15: error: missing 'template' keyword prior to dependent template name 'get'"
Take the following peice of c++ code, which compiles fine (gcc 10.1.0) : -
#include <iostream>
#include <string>
#include <functional>
template <class T = std::string>
void foo(T src, std::function<void(T&& t)> completionFn)
{
completionFn(std::move(src));
}
int main(int argc, char *argv[])
{
foo<std::string>("hello", [] (auto && t) {
std::cout << t << std::endl;
});
return 0;
}
If I modify the main function to remove the template parameter in the call to "foo", it no longer compiles even though I have a default template parameter, and I cannot work out why.
int main(int argc, char *argv[])
{
foo<>("hello", [] (auto && t) {
std::cout << t << std::endl;
});
return 0;
}
I am probably missing something obvious.
Here is the compiler output : -
src/scanner_test.cpp: In function ‘int main(int, char**)’:
src/scanner_test.cpp:19:6: error: no matching function for call to ‘foo(const char [6], main(int, char**)::<lambda(auto:11&&)>)’
19 | });
| ^
src/scanner_test.cpp:10:6: note: candidate: ‘template<class T> void foo(T, std::function<void(T&&)>)’
10 | void foo(T src, std::function<void(T&& t)> completionFn)
| ^~~
src/scanner_test.cpp:10:6: note: template argument deduction/substitution failed:
src/scanner_test.cpp:19:6: note: ‘main(int, char**)::<lambda(auto:11&&)>’ is not derived from ‘std::function<void(T&&)>’
19 | });
What am I missing? Thanks! Apologies if it's a silly question.
The default template parameter is only used if the template CANNOT be determined from context. In the context given foo<>("hello", ...) the template T is determined to be const char [6] (as given in the error message). For functions this will always be the case for template parameters that relate to real parameters in the function.
The solution you maybe looking for is:
#include <iostream>
#include <string>
#include <functional>
template <class T>
void foo(T src, std::function<void(std::string&& t)> completionFn)
{
//NOTE cast here to std::string, ensures we always have an std::string
completionFn(std::move((std::string&)src));
}
int main(int argc, char *argv[])
{
foo("hello", [] (std::string&& t) {
std::cout << t << std::endl;
});
return 0;
}
While exploring templates in C++, I stumbled upon the example in the following code:
#include <iostream>
#include <functional>
template <typename T>
void call(std::function<void(T)> f, T v)
{
f(v);
}
int main(int argc, char const *argv[])
{
auto foo = [](int i) {
std::cout << i << std::endl;
};
call(foo, 1);
return 0;
}
To compile this program, I am using the GNU C++ Compiler g++:
$ g++ --version // g++ (Ubuntu 6.5.0-1ubuntu1~16.04) 6.5.0 20181026
After compiling for C++11, I get the following error:
$ g++ -std=c++11 template_example_1.cpp -Wall
template_example_1.cpp: In function ‘int main(int, const char**)’:
template_example_1.cpp:15:16: error: no matching function for call to ‘call(main(int, const char**)::<lambda(int)>&, int)’
call(foo, 1);
^
template_example_1.cpp:5:6: note: candidate: template<class T> void call(std::function<void(T)>, T)
void call(std::function<void(T)> f, T v)
^~~~
template_example_1.cpp:5:6: note: template argument deduction/substitution failed:
template_example_1.cpp:15:16: note: ‘main(int, const char**)::<lambda(int)>’ is not derived from ‘std::function<void(T)>’
call(foo, 1);
^
(same for C++14 and C++17)
From the compiler error and notes I understand that the compiler failed to deduce the type of the lambda, since it cannot be matched against std::function.
Looking at previous questions (1, 2, 3, and 4) regarding this error, I am still confused about it.
As pointed out in answers from questions 3 and 4, this error can be fixed by explicitly specifying the template argument, like so:
int main(int argc, char const *argv[])
{
...
call<int>(foo, 1); // <-- specify template argument type
// call<double>(foo, 1) // <-- works! Why?
return 0;
}
However, when I use other types instead of int, like double, float, char, or bool, it works as well, which got me more confused.
So, my questions are as follow:
Why does it work when I explicitly specify int (and others) as the template argument?
Is there a more general way to solve this?
A std::function is not a lambda, and a lambda is not a std::function.
A lambda is an anonymous type with an operator() and some other minor utility. Your:
auto foo = [](int i) {
std::cout << i << std::endl;
};
is shorthand for
struct __anonymous__type__you__cannot__name__ {
void operator()(int i) {
std::cout << i << std::endl;
}
};
__anonymous__type__you__cannot__name__ foo;
very roughly (there are actual convert-to-function pointer and some other noise I won't cover).
But, note that it does not inherit from std::function<void(int)>.
A lambda won't deduce the template parameters of a std::function because they are unrelated types. Template type deduction is exact pattern matching against types of arguments passed and their base classes. It does not attempt to use conversion of any kind.
A std::function<R(Args...)> is a type that can store anything copyable that can be invoked with values compatible with Args... and returns something compatible with R.
So std::function<void(char)> can store anything that can be invoked with a char. As int functions can be invoked with a char, that works.
Try it:
void some_func( int x ) {
std::cout << x << "\n";
}
int main() {
some_func('a');
some_func(3.14);
}
std::function does that some conversion from its signature to the callable stored within it.
The simplest solution is:
template <class F, class T>
void call(F f, T v) {
f(v);
}
now, in extremely rare cases, you actually need the signature. You can do this in c++17:
template<class T>
void call(std::function<void(T)> f, T v) {
f(v);
}
template<class F, class T>
void call(F f_in, T v) {
std::function f = std::forward<F>(f_in);
call(std::move(f), std::forward<T>(v));
}
Finally, your call is a crippled version of std::invoke from c++17. Consider using it; if not, use backported versions.
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.
I intend to use shared_ptr quite a bit in an upcoming project, so (not being aware of std::make_shared) I wanted to write a variadic template function spnew<T>(...) as a shared_ptr-returning stand-in for new. Everything went smoothly till I attempted to make use of a type whose constructor includes an initializer_list. I get the following from GCC 4.5.2 when I try to compile the minimal example below:
In function 'int main(int, char**)':
too many arguments to function 'std::shared_ptr spnew(Args ...) [with T = Example, Args = {}]'
In function 'std::shared_ptr spnew(Args ...) [with T = Example, Args = {}]':
no matching function for call to 'Example::Example()'
Oddly enough, I get equivalent errors if I substitute std::make_shared for spnew. In either case, it seems to be incorrectly deducing the parameters when an initializer_list is involved, erroneously treating Args... as empty. Here's the example:
#include <memory>
#include <string>
#include <vector>
struct Example {
// This constructor plays nice.
Example(const char* t, const char* c) :
title(t), contents(1, c) {}
// This one does not.
Example(const char* t, std::initializer_list<const char*> c) :
title(t), contents(c.begin(), c.end()) {}
std::string title;
std::vector<std::string> contents;
};
// This ought to be trivial.
template<class T, class... Args>
std::shared_ptr<T> spnew(Args... args) {
return std::shared_ptr<T>(new T(args...));
}
// And here are the test cases, which don't interfere with one another.
int main(int argc, char** argv) {
auto succeeds = spnew<Example>("foo", "bar");
auto fails = spnew<Example>("foo", {"bar"});
}
Is this just an oversight on my part, or a bug?
You could do this -
#include <memory>
#include <string>
#include <iostream>
#include <vector>
struct Example {
template<class... Args>
Example(const char* t, Args... tail) : title(t)
{
Build(tail...);
}
template<class T, class... Args>
void Build(T head, Args... tail)
{
contents.push_back(std::string(head));
Build(tail...);
}
template<class T>
void Build(T head)
{
contents.push_back(std::string(head));
}
void Build() {}
std::string title;
std::vector<std::string> contents;
};
template<class T, class... Args>
std::shared_ptr<T> spnew(Args... args) {
return std::shared_ptr<T>(new T(args...));
}
int main(int argc, char** argv) {
auto succeeds = spnew<Example>("foo", "bar");
auto fails = spnew<Example>("foo", "bar", "poo", "doo");
std::cout << "succeeds->contents contains..." << std::endl;
for ( auto s : succeeds->contents ) std::cout << s << std::endl;
std::cout << std::endl << "fails->contents contains..." << std::endl;
for ( auto s : fails->contents ) std::cout << s << std::endl;
}
This, despite the generic templates is type safe as the compiler will complain about
the contents.push_back if the passed type is not convertible to a const char *.
As described above, your code was working fine with gcc 4.6 however the warning you get is explained here
why-doesnt-my-template-accept-an-initializer-list, and is possibly not standards
compliant, although the c++0x standard is yet to be published so this could change.
With gcc-4.7 (probably would work on gcc-4.6 too, just branched) with warnings:
foo.cpp: In function ‘int main(int, char**)’:
foo.cpp:29:47: warning: deducing ‘Args ...’ as ‘std::initializer_list<const
char*>’ [enabled by default]
foo.cpp:22:20: warning: in call to ‘std::shared_ptr<_Tp1> spnew(Args ...)
[with T = Example, Args = {const char*, std::initializer_list<const
char*>}]’ [enabled by default]
foo.cpp:29:47: warning: (you can disable this with -fno-deduce-init-list)
[enabled by default]
I'm not sure why anyone would want to beef about init-list deduction though.
There is a related thread:
Why doesn't my template accept an initializer list
Basically, a bare init-list doesn't have a type.