Why can't we pass strings as template arguments? - c++

I know we can define templates using constants. For instance:
template<int N>
struct FixedArray {
double values[N];
int size() { return N; } // Could be static
};
int main(int, char**) {
FixedArray<10> arr;
arr.values[0] = 3.14;
cout << "first element=" << arr.values[0] << endl;
cout << "size=" << arr.size() << endl;
return 0;
}
This specific example lets us define an array with a constant size.
But why can't we pass strings as template arguments in C++?
The following slide is suppose to explain it but I'm not getting where the problem is.
If someone can point it out to me and explain it I'd appreciate it.
Thanks

The short answer is, "because the standard says so". Since template arguments serve to form a type, they must be sufficiently unambiguous. The following works, though:
template <char *> struct Foo { };
char x;
int main()
{
Foo<&x> a;
}
The point is that x is now a well-defined, named object with linkage, so its address is a globally, sta­ti­cally known quantity. The pointer derived from a string literal does not have the same qualities; it is not the address of a named variable.

Related

How to use some type specific function (such as .size() for std::string or std::vector and so on) on something of template type?

If possible, how could I use some type-specific function (such as .size() for: std::string or std::vector or ...) in a function with a template type, being sure that when I'll use that type-specific function I'm actually calling it with the correct type as argument? Maybe I'm wrong, and if it is, please explain to me what I have to do.
#include <iostream>
#include <string>
template <typename T>
std::string func(T& number) {
if (typeid(T) == typeid(std::string)) {
unsigned short int size = number.size();// !
return " is a string";
}
else if (typeid(T) == typeid(int)) {
return " is an int";
}
//...
}
int main() {
std::string name = "Anthony";
int age = 8;
std::cout << name /*<< func(name) */<< '\n' << age << func(age) << '.';
return 0;
}
I know that in the code above the line:
unsigned short int size = number.size();//(I need exactly '.size()')
doesn't make any sense (even the whole code doesn't make much sense) considering that I never use that value, but to find the size of the string (when it is a string!) is exactly what I need, and to not post a very long code that would make sense, I'm posting only this to make it give the error I've had when trying to compile, and in order to give you a minimal reproducible example. So please, don't say to me "just delete that line and your code will work").
Instead of if (typeid(T) == typeid(std::string)), use if constexpr (std::is_same_v<T, std::string>). ( Similarly, else if constexpr instead of else if).
Regular if requires both branches to be valid, even if the condition is known at compile-time. if constexpr requires a compile-time condition, but allows the discarded branch to be invalid (only if the error is related to the template argument; every branch has to be theoretically valid for some template argument).
std::is_same_v<T, std::string> is similar to typeid(T) == typeid(std::string), except it counts as a compile-time constant. if constexpr would reject the latter.
If you really need to use a template here, simply specialize the template.
template <typename T>
std::string func(T& number);
template<>
std::string func<std::string>(std::string& number) {
unsigned short int size = number.size();// !
return " is a string";
}
template<>
std::string func<int>(int& number) {
return " is an int";;
}
Usually you using a template you want to avoid using specific implementations for types though. Overloads would be preferrable for a limited set of types using type-specific implementations though.
Since your requirement is not restricted to std::string(as you have mentioned std::vector etc), you can use SFINAE as shown below:
#include <iostream>
#include<typeinfo>
#include<string>
#include<vector>
#include <type_traits>
//make sure that this overload is added to the set when T has a size() member function which is your requirement
template<typename T>
auto func(T const& number) -> decltype((void)(number.size()), std::string())
{
auto size = number.size();
return " is a " + std::string(typeid(number).name());
}
template<typename T>
std::enable_if_t<std::is_fundamental_v<std::remove_reference_t<T>>,std::string> func(T const& number)
{
return " is a " + std::string(typeid(number).name());
}
int main()
{
std::string name = "Anthony";
int age = 8;
double dage = 22.2;
std::cout << name << func(name) << '\n' << age << func(age) << '.'<<"\n"<<dage << func(dage);
//lets test it with vector
std::vector<int> vec= {1,2,3};
std::cout<<"\nvec: "<< func(vec);
return 0;
}
Demo
The output of the above program can be seen here:
Anthony is a NSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE
8 is a i.
22.2 is a d
vec: is a St6vectorIiSaIiEE

Static class member

What's wrong with the code below? Latest version of g++ and clang both give error. I am sure I am missing something basic here.
#include <iostream>
struct Z
{
static const int mysize = 10;
};
Z f2();
int main()
{
std::cout << f2()::mysize << std::endl;
}
The motivation here is to be able to find out the size of an array using templates using code such as below. I know there are many ways, but just stumbled upon this idea.
template<int N> struct S
{
enum { mysize = N };
};
template<class T, int N> S<N> f(T (&)[N]);
int main()
{
char buf[10];
std::cout << f(buf)::mysize << std::endl;
}
f2() returns a value, not a type. You'd need to use the . operator on the return value instead of ::
The :: operator requires a type to be named on the lefthand side, while . allows for a value to be named. Your expression f2() does not name a type so it cannot be used in conjunction with ::.
As a side note, with a little more detail in the question we might be able to solve your real problem.
Your program contains two mistakes:
You are using the :: operator to access the member of an object. Use operator . ("dot") instead;
You declare function f2() and invoke it without defining it (this will give you a linker error).
Also, since static member variables are shared among all instances of a class (Z in this case), you do not need an object to access it;
Here is how you could fix your program:
#include <iostream>
struct Z
{
static const int mysize = 10;
};
Z f2() { return Z(); }
int main()
{
// Don't need an object to access a static variable...
std::cout << Z::mysize << std::endl;
// ...but if you really want to, do it this way...
std::cout << f2().mysize << std::endl;
}
Why don't you use this way to find out the size of array by templates:
#include <iostream>
template<int N> struct S
{
enum { mysize = N };
};
template<class T, int N> int f1(T (&)[N])
{
return N;
}
int main()
{
char buf[10];
std::cout << f1(buf) << std::endl;
}
And this one is closer to your variant:
template<class T, int N> S<N> f(T (&)[N])
{
S<N> foo;
return foo;
}
int main()
{
char buf[10];
std::cout << f(buf).mysize << std::endl;
}
Anyway, you will need to return an object from f and access it's member by ., not by ::.
But it's more probable that second variant will be slower, because first variant is fully compile-time, but in the second variant compiler may miss the optimization and don't optimize out the run-time creation of foo.
I think you need to add const int Z::mysize; after class declaration.

Template in C++, why have to use enum

I have a quick question about item 48 in Scott Meyers' "Effective C++".
I just don't understand the code copied from the book below,
#include <iostream>
using namespace std;
template <unsigned n>
struct Factorial
{
enum { value=n*Factorial<n-1>::value };
};
template <>
struct Factorial<0>
{
enum { value=1};
};
int main()
{
cout<<Factorial<5>::value<<endl;
cout<<Factorial<10>::value<<endl;
}
Why do I have to use enum in template programming?
Is there an alternative way to do this?
Thanks for the help in advance.
You could use static const int also:
template <unsigned n>
struct Factorial
{
static const int value= n * Factorial<n-1>::value;
};
template <>
struct Factorial<0>
{
static const int value= 1;
};
This should be fine also. The result is same in both cases.
Or you could use existing class template, such as std::integral_constant (in C++11 only) as:
template <unsigned n>
struct Factorial : std::integral_constant<int,n * Factorial<n-1>::value> {};
template <>
struct Factorial<0> : std::integral_constant<int,1> {};
I see that the other answers cover the alternative approaches well, but no one's explained why the enum (or static const int) is required.
First, consider the following non-template equivalent:
#include <iostream>
int Factorial(int n)
{
if (n == 0)
return 1;
else
return n * Factorial(n-1);
}
int main()
{
std::cout << Factorial(5) << std::endl;
std::cout << Factorial(10) << std::endl;
}
You should be able to understand it easily. However, it's disadvantage is that the value of the factorial will be computed at run-time, i.e. after running your program the compiler will execute the recursive function calls and calculations.
The idea of template approach is to perform the same calculations at compile-time, and place the result in the resulting executable. In other words, the example you presented resolves to something alike:
int main()
{
std::cout << 120 << std::endl;
std::cout << 3628800 << std::endl;
}
But in order to achieve that, you have to 'trick' the compiler into performing the computations. And in order to do that, you need to let it store the result somewhere.
The enum is there exactly in order to do that. I will try to explain that by pointing out what would not work there.
If you tried to use a regular int, it would not work because a non-static member like int is meaningful only in a instantiated object. And you can't assign a value to it like this but instead do that in a constructor. A plain int won't work.
You need something that would be accessible on an uninstantiated class instead. You could try static int but it still doesn't work. clang would give you a pretty straightforward description of the problem:
c.cxx:6:14: error: non-const static data member must be initialized out of line
static int value=n*Factorial<n-1>::value ;
^ ~~~~~~~~~~~~~~~~~~~~~~~
If you actually put those definitions out-of-line, the code will compile but it will result in two 0s. That is because this form delays the computation of values to the initialization of program, and it does not guarantee the correct order. It is likely that a Factorial<n-1>::values was obtained before being computed, and thus 0 was returned. Additionally, it is still not what we actually want.
Finally, if you put static const int there, it will work as expected. That's because static const has to be computed at the compile time, and that's exactly what we want. Let's type the code again:
#include <iostream>
template <unsigned n>
struct Factorial
{
static const int value=n*Factorial<n-1>::value ;
};
template <>
struct Factorial<0>
{
static const int value=1;
};
int main()
{
std::cout << Factorial<5>::value << std::endl;
std::cout << Factorial<10>::value << std::endl;
}
First you instantiate Factorial<5>; static const int forces the compiler has to compute its value at compiler time. Effectively, it instantiates the type Factorial<4> when it has to compute another value. And this goes one until it hit Factorial<0> where the value can be computed without further instantiations.
So, that was the alternate way and the explanation. I hope it was at least a bit helpful in understanding the code.
You can think of that kind of templates as a replacement of the recursive function I posted at the beginning. You just replace:
return x; with static const int value = ...,
f(x-1) with t<x-1>::value,
and if (n == 0) with the specialization struct Factorial<0>.
And for the enum itself, as it was already pointed out, it was used in the example to enforce the same behavior as static const int. It is like that because all the enum values need to be known at compile-time, so effectively every requested value has to be computed at compile-time.
To be more specific, the "enum hack" exists because the more correct way of doing it with static const int was not supported by many compilers of the time. It's redundant in modern compilers.
You can use static const int as Nawaz says. I guess the reason Scott Myers uses an enum is that compiler support for in-class initialization of static const integers was a bit limited when he wrote the book. So an enum was a safer option.
You can use an int instead of static const it for this as follows:
template<int n>
struct Factorial
{
int val{ n*Factorial<n - 1>().val };
};
template<>
struct Factorial<0>
{
int val{1};
};
int main()
{
cout << "Factorial 5: " << Factorial<5>().val << endl;
}
You can also use function template instead of struct/class template:
template<int n>
int fact()
{
return n*fact<n - 1>();
}
template <>
int fact<0>()
{
return 1;
}
int main()
{
cout << "Fact 5: " << fact<5>() << endl;
}

nontypes template parameter

I have learned that:
Nontype template parameters carry some restrictions. In general, they may be constant integral values (including enumerations) or pointers to objects with external linkage.
So i made following code
1.
template <char const* name>
class MyClass {
…
};
char const* s = "hello";
MyClass<s> x; // ERROR:
This code didn't work and produce error 's' is not a valid template argument
My second code also didn't work
2.
template <char const* name>
class MyClass {
…
};
extern char const *s = "hello";
MyClass<s> x; //error 's' is not a valid template argument`
But strangely this code is fine
3.
template <char const* name>
class MyClass {
…
};
extern char const s[] = "hello";
MyClass<s> x; // OK
please tell what is happening in all of these three codes??
also tell how to correct errors to make other two codes working also.
From here: "Non-type template argument provided within a template argument list is an expression whose value can be determined at compile time".
You get a problem because your char pointer is not really constant in the first two examples. Have a look at this short example:
int main() {
char const *c = "foor";
std::cout << "c: " << c << std::endl;
c = "bar";
std::cout << "c: " << c << std::endl;
}
Which will give you
c: foo
c: bar
I think the problem is here:
even
const char * const p="hello";
only define a pointer variable which stores the address of a memory, the value of the memory cannot be determined when compilation.
but
const char pp[]="hello";
the compiler will know when compile the memory is "hello", not a pointer to somewhere else.
that's why
printf(" p=%p, &p=%p\n", p, &p);
will get the same value.
but
printf("pp=%p, &pp=%p\n", pp, &pp);
will not show the same value.

Why can't a class have same name for a function and a data member?

Why can't a c++ class have same name for a function and a data member?
class demo{
public:
int size();
private:
int size;
};
int main(){
return 0;
}
C:\Users\S>g++ demo.c
demo.c:5:7: error: declaration of 'int demo::size'
demo.c:3:7: error: conflicts with previous declaration 'int demo::size()'
Suppose you want to take the address of the member-function size(), then you would write this:
auto address = &demo::size;
But it could be very well be the address of the member-data size as well. Ambiguous situation. Hence, it is disallowed by the language specification.
That is not to say that it was impossible for the C++ committee to come up with a solution, but I suppose there is no major gain in doing so. Hence, the Standard simply disallowed it, to keep things simple.
Also, the difference between member-data and member-function becomes less distinguishable visually if one declares the member function size() as:
typedef void fun_type();
struct demo
{
fun_type size; //It looks like a member-data, but it's a member-function
};
void demo::size() //define the member function
{
std::cout << "It is crazy!" << std::endl;
}
int main()
{
demo d;
d.size(); //call the function!
}
Output:
It is crazy!
See the online demo : http://ideone.com/ZjwyJ
Now if we can implement member functions as explained above, then it becomes too obvious even to the naked eye that you cannot add another member with same name as:
struct demo
{
fun_type size;
int size; //error - choose a different name for the member!
};
Wait That is not entirely correct, as the story is not finished yet. There is something less obvious I need to add here. You can add more than one member with same name:
typedef void fun_type0();
typedef void fun_type1(int a);
typedef void fun_type2(int a, int b);
struct demo
{
fun_type0 member; //ok
fun_type1 member; //ok
fun_type2 member; //ok
};
This is completely valid code, as each member is a function of different type, so you can define them as:
void demo::member()
{
std::cout << "member()" << std::endl;
}
void demo::member(int a)
{
std::cout << "member(" << a << ")" << std::endl;
}
void demo::member(int a, int b)
{
std::cout << "member(" << a << ", "<< b << ")" << std::endl;
}
Test code:
int main()
{
demo d;
d.member();
d.member(10);
d.member(200,300);
}
Output:
member()
member(10)
member(200, 300)
Online Demo : http://ideone.com/OM97Q
The conclusion...
You can add members with same name, as long as they're function of different types. This is enabled by a feature called member-function-overloading (or simple function-overloading)1.
1. Unfortunately, the language doesn't provide similar feature, say member-data-overloading, for member data, neither do the language provide cross-member-overloading (that allows member-data and member-function to have the same name — the case in the question).
So here a question naturally arises: do they not cause ambiguity problem? Yes, they do. But the point to be noted is that C++ committee came up with a solution to solve this ambiguity-problem, because they saw a huge gain in doing so, (in case of function-overloading).
But the case in the question remains ambiguous, as the committee didn't come up with a solution, as they didn't see any huge advantage in doing so (as noted before). Also, when I said "C++ committee came up with solution", I do NOT mean that the solution has been Standardized, I merely mean that they knew how the compilers can solve it, and how complex the solution would be.
because if you use size in your class somewhere then the compiler does not know what to do. It can be either the int-data-member or it can be the function-pointer. So the compiler is not able to seperate both kind
As an example (Not maybe the best but it might explain it visually):
class Size {
std::size_t size_;
public:
Size(std::size_t s = std::size_t() ) : size_(s){}
std::size_t operator()() const {
return size_;
}
void operator()(std::size_t s) {
size_ = s;
}
};
class Demo {
public:
Size size;
};
int main() {
Demo d;
d.size(10);
std::size_t size = d.size();
return 0;
}
Basically the variable could be callable as well. So there is no way for the compiler to know your intentions.
Of course this is defined by the language that it shall not be possible to have the same name as identifier within the same scope.