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.
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;
}
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.
Is it possible in C++11 to overload const char*'s and string literals (const char[])?
The idea is to avoid having to call strlen to find the string length when this length is known already.
This snippet breaks on G++ 4.8 and Clang++ 3.2:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
template<typename T, int N>
void length(const T(&data)[N]) {
printf("%u[]\n", N - 1);
}
template<typename T>
void length(const T* data) {
printf("*%u\n", (unsigned)strlen(data));
}
int main() {
length("hello");
const char* p = "hello";
length(p);
return 0;
}
Error (Clang):
test2.cpp:16:3: error: call to 'length' is ambiguous
length("hello");
^~~~~~
test2.cpp:6:6: note: candidate function [with T = char, N = 6]
void length(const T(&data)[N]) {
^
test2.cpp:11:6: note: candidate function [with T = char]
void length(const T* data) {
^
1 error generated.
Hacked a bit, and this appears to work:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
template<typename T, int N>
void length(const T(&data)[N]) {
printf("%u[]\n", N - 1);
}
template<typename T>
void length(T&& data) {
printf("*%u\n", (unsigned)strlen(data));
}
const char *foo() {
return "bar";
}
int main() {
length("hello");
const char* p = "hello";
length(p);
length(foo());
return 0;
}
Is this valid C++11? The string literal appears to overload on T&& when the array specialization is removed. What causes this ambigousness to be resolved, but not the one in the first code snippet?
In the first case, during overload resolution you have a perfect match requiring no conversion against an array to pointer conversion (which is in the category "lvalue transformation", along with lvalue to rvalue and function to pointer conversion). A difference that is only made by an lvalue transformation is not sufficient for overload resolution to pick a winner.
In the second case, during overload resolution, both functions have the exact same parameter type. Then partial ordering as the last resort finds that the second template would accept all arguments you ever pass to it, wheras the first template only accepts arrays. Therefor the first template in the second case is found more specialized and taken.
As for your other question - no, overloading specifically for string literals is not possible. You are always going to catch arrays of the same size along with them.
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.