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.
Related
Here is an example of the problem I am having:
#include <stdio.h>
#include <iostream>
template<std::size_t U, std::size_t V>
void func2(int (&twoDArrayA)[U][V], const int shift){
const int length = 1 << shift;
int twoDArrayB[length][length]; //Successful
}
//template<std::size_t A> <-- Tried to solve the problem by adding this
void func1(const int shift){
const int length = 1 << shift;
int twoDArrayA[length][length]; //Failed
func2(twoDArrayA,shift);
}
int main() {
const int shift = 3;
func1(shift);
}
Error message:
error: no matching function for call to 'func2(int [length][length], const int&)'
template argument deduction/substitution failed:
variable-sized array type 'int' is not a valid template argument
I thought it is because of the use of the template before the func2, so I tried to do the same thing on func1. The attempt of making the call to func1 fails instead. Error message:
error: no matching function for call to 'func1(const int&)'
template argument deduction/substitution failed:
couldn't deduce template parameter 'A'
Is there any way I can pass such an argument as twoDArrayA to func2?
func2 is failing to deduce the array size because it isn't known at compile time; length is being decided at runtime based on the argument you pass to func1. For the pass-by-reference to work with template arguments and deduction, you will need to have a 2D array with defined size at compile time, for example, int arr[8][8].
It looks like the code you're working on wants to decide the array size in func1 based on shift and then pass that array to func2. You might consider designing func2 to take an int** and then access it as you would a 2D array, based on the result of 1<<shift:
void func2(int** twoDArrayA, const int shift) {
const int length = 1 << shift;
int last_item = twoDArrayA[length-1][length-1]
}
You might also find some more helpful resources here!
I have encounter serious template type deduction problem when I use method pointer in argument of a template function.
Let's take the following code:
template <class ClassT, typename Arg1T>
inline void testTemplateFct(ClassT * clazz,
void (ClassT::*fctPtr)(Arg1T),
const Arg1T & arg1)
{
}
class TestClass
{
public:
void testMethodIntArg(int arg)
{}
void testMethodDoubleArg(double arg)
{}
void testMethodStringArg(const char * arg);
};
int main()
{
TestClass testClass;
testTemplateFct(&testClass,
&TestClass::testMethodIntArg,
10);
testTemplateFct(&testClass,
&TestClass::testMethodDoubleArg,
10.0);
/// BEGINNING OF MY PROBLEM
testTemplateFct(&testClass,
&TestClass::testMethodStringArg,
"a string...");
/// END OF MY PROBLEM
return 0;
}
If I compile it using g++, I get the following error message:
$ g++ ArgumentDeduction.cpp -o ArgumentDeduction
ArgumentDeduction.cpp: In function ‘int main()’:
ArgumentDeduction.cpp:42:18: error: no matching function for call to ‘testTemplateFct(TestClass*, void (TestClass::*)(const char*), const char [12])’
"a string...");
^
ArgumentDeduction.cpp:4:13: note: candidate: template<class ClassT, class Arg1T> void testTemplateFct(ClassT*, void (ClassT::*)(Arg1T), const Arg1T&)
inline void testTemplateFct(ClassT * clazz,
^~~~~~~~~~~~~~~
ArgumentDeduction.cpp:4:13: note: template argument deduction/substitution failed:
ArgumentDeduction.cpp:42:18: note: deduced conflicting types for parameter ‘const Arg1T’ (‘const char*’ and ‘char [12]’)
"a string...");
If I remove the reference of the third argument of method testTemplateFct the problem disappears (HOWEVER I ABSOLUTELY NEED THE REFERENCE IN ORDER TO AVOID COPY)
template <class ClassT, typename Arg1T>
inline void testTemplateFct(ClassT * clazz,
void (ClassT::*fctPtr)(Arg1T),
const Arg1T arg1)
{}
I understand more or less the error message but I do not understand why there is an ambiguity between const char* and char [12]. I do not understand why the problem disappears when I remove the reference.
Finally, I would strongly appreciate any help in order to correct this code while keeping the reference
PS: I know that I can "force" the type deduction by doing:
testTemplateFct(&testClass,
&TestClass::testMethodStringArg,
(const char *) "a string...");
but I don't like it very much
Your template requires that both the occurrences of Arg1T are deduced to the same type. I believe that is not what you want. Instead the types should be deduced independently:
template <class ClassT, typename Arg1T, typename GivenT>
inline void testTemplateFct(ClassT * clazz,
void (ClassT::*fctPtr)(Arg1T),
GivenT &&arg1)
{
//example use
(clazz->*fctPtr)(std::forward<GivenT>(arg1));
}
I do not understand why there is an ambiguity between const char* and char [12].
Note that "a string..." is an array with type const char[12]. For the function template testTemplateFct, the parameter arg1 is declared as a reference, i.e. const Arg1T &, then array-to-pointer decay won't occur in template argument deduction and Arg1T is deduced as char[12], which doesn't match the deduced type of Arg1T from the 2nd argument, i.e. const char*, so deduction failed.
I do not understand why the problem disappears when I remove the reference.
When the parameter is declared as pass-by-value array-to-pointer decay is applied; then both the deduced type of Arg1T from the 2nd and 3rd argument will be const char* and everything work fine.
You have two basic options.
The first one is to change your invocation to:
testTemplateFct(&testClass,
&TestClass::testMethodStringArg,
(const char *)"a string...");
The second option is to add an overload:
template <class ClassT, size_t n>
inline void testTemplateFct(ClassT * clazz,
void (ClassT::*fctPtr)(const char *),
const char (&arg1)[n])
{
testTemplateFct<ClassT, const char *>(clazz, fctPtr, arg1);
}
Pick which one works best for you.
A literal character string is actually a const char[n], and not a const char *. The const char array decays to a const char * in an ordinary function call; but this decay does not occur as part of template deduction; hence the problem.
Take this example:
#include <vector>
#include <algorithm>
#include <iostream>
#include <cstdlib>
#include <cmath>
void PrintVec(const std::vector<float>&);
int main(int argc, char * argv[]){
float vals[] = {-1.2,0.0,1.2};
std::vector<float> test(vals, vals + sizeof(vals) / sizeof(float));
std::vector<float> absTest(3);
std::transform(test.begin(), test.end(), absTest.begin(), std::abs<float>());
PrintVec(test);
PrintVec(absTest);
return 0;
}
void PrintVec(const std::vector<float> &vec){
for (unsigned int i = 0; i < vec.size(); ++i){
std::cout << vec[i] << '\n';
}
return;
}
Using both gcc 4.3.4 and VS 2013 I get compiler errors. For gcc its:
testTransformAbs.cpp:15: error: expected primary-expression before 'float'
For VS 2013 its:
error C2062: type 'float' unexpected
If I remove <float> then I get this error:
testTransformAbs.cpp:15: error: no matching function for call to 'abs()'
/usr/include/stdlib.h:766: note: candidates are: int abs(int)
/usr/include/c++/4.3/cstdlib:144: note: long int std::abs(long int)
/usr/include/c++/4.3/cstdlib:175: note: long long int __gnu_cxx::abs(long long int)
/usr/include/c++/4.3/cmath:99: note: double std::abs(double)
/usr/include/c++/4.3/cmath:103: note: float std::abs(float)
/usr/include/c++/4.3/cmath:107: note: long double std::abs(long double)
I can create my own function
float MyAbs(float f){
return sqrt(f * f);
}
std::transform(test.begin(), test.end(), absTest.begin(), MyAbs);
And everything works. The reference on cplusplus.com says that the fourth input can be an UnaryOperation defined by:
Unary function that accepts one element of the type pointed to by InputIterator as argument, and returns some result value convertible to the type pointed to by OutputIterator.
This can either be a function pointer or a function object.
To me this should be able to use std::abs(). I also tried fabs with the same result. What am I missing?
std::abs is an overloaded function, not a template function. When obtaining a pointer to the function, you can choose a specific overload by casting:
std::transform(test.begin(), test.end(), absTest.begin(),
static_cast<float (*)(float)>(&std::abs));
or by using a function pointer variable:
float (*fabs)(float) = &std::abs;
std::transform(test.begin(), test.end(), absTest.begin(), fabs);
Note that I also removed the () you put after abs, since this is a function and not a class that needs to be instantiated.
std::abs is not a template. Any function in headers prefixed with a c like cmath or cstdlib do not have any C++ features like templates, because they represent the C standard library.
Also std::abs is for integral types. You should be using std::fabs for floating point types.
I dont like function pointer casts, so in cases like this one, i usually write some wrappers like these:
namespace hlp {
template <class T> struct iabs {
static_assert(std::is_integral<T>::value, "");
T operator()(T const& t){ return std::abs(t); }
};
template <class T> struct fabs {
static_assert(std::is_floating_point<T>::value, "");
T operator()(T const& t){ return std::fabs(t); }
};
}
You can use those wrappers like you wanted to use std::abs in your question.
The static_assert will generate a clean compiler error when you try to use the integral version for floating point types or vice versa.
Question:
In the code below, template argument type deduction seems to fail for the first sample, but not for the second sample. I don't understand why the first sample fails to deduce T = char. I would think that T can be deduced when converting from "foo" to std::bacis_string<T>, but even if that didn't work, I provide the second function argument which, I would think, would clearly constrain T to char. Why does it fail?
Does not work:
#include <iostream>
#include <string>
template <typename T>
void print(const std::basic_string<T>& a, const std::basic_string<T>& b)
{
std::cout << a << b << std::endl;
}
int main()
{
std::string bar = "bar";
print("foo", bar);
}
Error:
string.cpp:14:5: error: no matching function for call to 'print'
print("foo", bar);
^~~~~
string.cpp:6:6: note: candidate template ignored: could not match
'basic_string<type-parameter-0-0, char_traits<type-parameter-0-0>,
allocator<type-parameter-0-0> >' against 'char const[4]'
void print(const std::basic_string<T>& a, const std::basic_string<T>& b)
^
1 error generated.
Works:
#include <iostream>
#include <string>
template <typename T>
void print(const std::basic_string<T>& a, const std::basic_string<T>& b)
{
std::cout << a << b << std::endl;
}
int main()
{
std::string foo = "foo";
std::string bar = "bar";
print(foo, bar);
}
The problem is a conversion is required here. To deduce T, the compiler would have to inspect all possible instantiations of std::basic_string and see which of them can be constructed from a const char* (or actually const char (&)[4]). That's of course not possible, as there's infinitely many of them. The reason why it has to inspect all and cannot just scan the primary template definition for constructors taking const char* or const char(&)[4] is that for some T, std::basic_string<T> could be partially or completely specialised, and the members of those specialisations have no relationship to the members of the primary template.
Here's the short version of an answer.
The compiler has char const[] and is looking to convert that to std::basic_string<T>. How does it work out what T is? You know that you want to match T = char but the compiler does not know that.
It could look for a constructor basic_string<T>(char const *), for example. Even if that exists, it still does not say what T should be.
The compiler doesn't iterate over all possible typenames it knows about and attempt basic_string<T> for each one, and then see if there is a matching constructor.
Similar example:
template<typename T>
struct Foo
{
Foo(T t) {}
};
int main()
{
Foo(0); // error, can't deduce Foo<int>
}
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.