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.
Related
This might be a silly question but I would like to have it clarified none the less. Lets say I have a template function like so:
template<class T> T getValue(const char *key) const;
that returns the value as T from internal storage where it is stored under key (and possibly as type T already).
Now in order to use this I need to specify the template return type T in the function call, for example:
int value = getValue<int>("myKey");
while what I would want it to do is deduce the template argument from the context, specifically the lvalue like so:
int value = getValue("myKey"); //getValue<int>() is instantiated with int being deduced automatically from lvalue
but I am guessing that this is not possible but I am rather fuzzy as to why. I know using auto would make it impossible for the compiler to deduce the template type but why this is as well?
Template instantiation can only deduce its parameters from the arguments to given templated object(function in this case) so no, the variable type does not matter in deducing, and you either have to provide dummy argument of type T to the function or hardcode it as you did in the second to last script code(getValue<int>(...)).
There is a possible workaround using type deduction presented in the comments :
#include <iostream>
namespace byte_read {
//this is a hack to deduce the type using implicit conversion
struct type_converter {
const char* buffer;
template<typename T>
operator T() {
std::cout << "implicit convertion from " << typeid(buffer).name()
<< " to " << typeid(T).name() << std::endl;
//casting memory to the desired type
return static_cast<T>(*buffer);
}
};
type_converter getValue(const char * buffer) {
//here buffer is implicitly converted to T type using the operator T()
return {buffer};
}
}
using namespace byte_read;
int main()
{
char buffer[]{0,1,0,0 //int 256 encoded
,97 //char 'a' encoded
};
//pointer to read the buffer sequentialy
char* pos = buffer;
//pointer used to count the bytes readed
char* last_pos = pos;
int int_256 = getValue(pos);
pos+=sizeof(int);
std::cout << int_256 << " bytes readed :" << pos - last_pos << std::endl;
last_pos = pos;
char char_a = getValue(pos);
pos+=sizeof(char);
std::cout << char_a << " bytes readed :" << pos - last_pos << std::endl;
}
You can try it here
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, statically known quantity. The pointer derived from a string literal does not have the same qualities; it is not the address of a named variable.
I don't understand why the array decays to a pointer in a template function.
If you look at the following code: When the parameter is forced to be a reference (function f1) it does not decay. In the other function f it decays. Why is the type of T in function f not const char (buff&)[3] but rather const char* (if I understand it correctly)?
#include <iostream>
template <class T>
void f(T buff) {
std::cout << "f:buff size:" << sizeof(buff) << std::endl; //prints 4
}
template <class T>
void f1(T& buff) {
std::cout << "f:buff size:" << sizeof(buff) << std::endl; //prints 3
}
int main(int argc, char *argv[]) {
const char buff[3] = {0,0,0};
std::cout << "buff size:" << sizeof(buff) << std::endl; //prints 3
f(buff);
f1(buff);
return 0;
}
It is because arrays cannot be passed by value to a function. So in order to make it work, the array decays into a pointer which then gets passed to the function by value.
In other words, passing an array by value is akin to initializing an array with another array, but in C++ an array cannot be initialized with another array:
char buff[3] = {0,0,0};
char x[3] = buff; //error
So if an array appears on the right hand side of =, the left hand side has to be either pointer or reference type:
char *y = buff; //ok - pointer
char (&z)[3] = buff; //ok - reference
Demo : http://www.ideone.com/BlfSv
It is exactly for the same reason auto is inferred differently in each case below (note that auto comes with C++11):
auto a = buff; //a is a pointer - a is same as y (above)
std::cout << sizeof(a) << std::endl; //sizeof(a) == sizeof(char*)
auto & b = buff; //b is a reference to the array - b is same as z (above)
std::cout << sizeof(b) << std::endl; //sizeof(b) == sizeof(char[3])
Output:
4 //size of the pointer
3 //size of the array of 3 chars
Demo : http://www.ideone.com/aXcF5
Because arrays can not be passed by value as a function parameter.
When you pass them by value they decay into a pointer.
In this function:
template <class T>
void f(T buff) {
T can not be char (&buff)[3] as this is a reference. The compiler would have tried char (buff)[3] to pass by value but that is not allowed. So to make it work arrays decay to pointers.
Your second function works because here the array is passed by reference:
template <class T>
void f1(T& buff) {
// Here T& => char (&buff)[3]
To quote from spec, it says
(14.8.2.1/2) If P is not a reference type: — If A is an array type,
the pointer type produced by the array-to-pointer standard conversion
(4.2) is used in place of A for type deduction; otherwise
So, in your case, It is clear that,
template <class T>
void f1(T& buff) {
std::cout << "f:buff size:" << sizeof(buff) << std::endl; //prints 3
}
doesn't decay into pointer.
Because functions can't have arrays as arguments. They can have array references though.
The reason basically boils down to type deduction when matching the different overloads. When you call f the compiler deduces the type to be const char[3] which then decays into const char* because that's what arrays do. This is done in the same exact way that in f(1) the compiler deduces T to be int and not int&.
In the case of f1 because the argument is taken by reference, then the compiler again deduces T to be const char[3], but it takes a reference to it.
Nothing really surprising, but rather consistent if it were not for the decay of arrays to pointers when used as function arguments...
Why can't you pass literal strings in here? I made it work with a very slight workaround.
template<const char* ptr> struct lols {
lols() : i(ptr) {}
std::string i;
};
class file {
public:
static const char arg[];
};
decltype(file::arg) file::arg = __FILE__;
// Getting the right type declaration for this was irritating, so I C++0xed it.
int main() {
// lols<__FILE__> hi;
// Error: A template argument may not reference a non-external entity
lols<file::arg> hi; // Perfectly legal
std::cout << hi.i;
std::cin.ignore();
std::cin.get();
}
Because this would not be a useful utility. Since they are not of the allowed form of a template argument, it currently does not work.
Let's assume they work. Because they are not required to have the same address for the same value used, you will get different instantiations even though you have the same string literal value in your code.
lols<"A"> n;
// might fail because a different object address is passed as argument!
lols<"A"> n1 = n;
You could write a plugin for your text editor that replaces a string by a comma separated list of character literals and back. With variadic templates, you could "solve" that problem this way, in some way.
It is possible, but the the template argument must have external linkage, which precludes using literal strings and mitigates the utility of doing this.
An example I have is:
template<const char* name, const char* def_value=empty_>
struct env : public std::string
{
env()
{
const char* p = std::getenv(name);
assign(p ? p : def_value);
}
};
extern const char empty_[] = "";
std::string test = env<empty_>();
This is how I do it. Makes a lot more sense to me:
struct MyString { static const std::string val; };
const std::string MyString::val = "this is your string";
template<typename T>
void func()
{
std::cout << T::val << std::endl;
}
void main()
{
func<MyString>();
}
Good question, thought I'd throw my hat into the ring... I guess you can pass pointers to static variables as non-type template arguments. From C++20 it looks like it won't be an issue... Until then, here is some cheap macro to make it work.
template <const char *Name, typename T>
struct TaggedValue {
static constexpr char const *name{Name};
T value;
friend ostream &operator<<(ostream &o, const TaggedValue &a) {
return o << a.name << " = " << a.value;
}
};
#define ST(name, type)\
const char ST_name_##name[]{#name};\
using name = TaggedValue<ST_name_##name,type>;
ST(Foo, int);
ST(Bar, int);
ST(Bax, string);
int main() {
cout << Foo{3} << endl;
cout << Bar{5} << endl;
cout << Bax{"somthing"} << endl;
}
C++20 comment (EDIT)
I've not used c++ much lately, so I'm sorry if this isn't 100% correct. There's a comment about why this wouldn't be an issue in c++20. According to the reference on template_parameters:
A non-type template parameter must have a structural type, which is one of the following types (optionally cv-qualified, the qualifiers are ignored):
...
a floating-point type;
a literal class type with the following properties:
all base classes and non-static data members are public and non-mutable and
the types of all base classes and non-static data members are structural types or (possibly multi-dimensional) array thereof.
This had led me to believe that the following code would work:
struct conststr
{
const char * const p;
template<std::size_t N>
constexpr conststr(const char(&a)[N]) : p(a)/*, sz(N - 1) */{}
};
template<conststr s>
struct A{};
int main(int argc, char **argv) {
A<conststr("foo")> x;
}
(Again, I'm not 100% sure if that's 100% correct). But it doesn't, at least not on my machine with g++ -std=c++2a (g++ --version == g++ (Debian 8.3.0-6) 8.3.0). It doesn't work with double either. This guy gives a much more detailed account of the history here, and there are probably better references, and I could just be completely incorrect.
This works for classes and, IMO, is useful. The implementation is quick and dirty but can easily be made cleaner:
#include <stdio.h>
#include <string.h>
struct TextTag { const char *text; };
template <const TextTag &TRUE, const TextTag &FALSE>
struct TextTaggedBool
{
const char *GetAsText() const { return m_value ? TRUE.text: FALSE.text; }
void SetByText(const char *s) { m_value = !strcmp(s, TRUE.text); }
bool m_value;
};
class Foo
{
public:
void method()
{
m_tbool.SetByText("True!"); printf("%s\n", m_tbool.GetAsText());
m_tbool.SetByText("False!"); printf("%s\n", m_tbool.GetAsText());
m_tbool.m_value = true; printf("%s\n", m_tbool.GetAsText());
m_tbool.m_value = false; printf("%s\n", m_tbool.GetAsText());
}
private:
static constexpr TextTag TrueTag = { "True!" };
static constexpr TextTag FalseTag = { "False!" };
TextTaggedBool<TrueTag, FalseTag> m_tbool;
};
void main() { Foo().method(); }
Output:
True!
False!
True!
False!
I'm trying to call a nonmember function of a derived class from a base class, but getting this error:
error: no matching function for call to 'generate_vectorlist(const char&)'
Here's the relevant code snippets from the base class:
//Element.cpp
#include "Vector.h"
...
string outfile;
cin >> outfile;
const char* outfile_name = outfile.c_str();
generate_vectorlist(*outfile_name); //ERROR
...
and the derived class (this is a template class, so everything's in the header):
//Vector.h
template <class T>
void generate_vectorlist(const char* outfile_name = "input.txt" )
{
std::ofstream vectorlist(outfile_name);
if (vectorlist.is_open())
for (Element::vciter iter = Element::vectors.begin(); iter!=Element::vectors.end(); iter++)
{
Vector<T>* a = new Vector<T>(*iter);
vectorlist << a->getx() << '\t' << a->gety() << '\t'<< a->getz() << std::endl;
delete a;
}
else { std::cout << outfile_name << " cannot be opened." << std::endl;}
vectorlist.close();
}
My guess is there's just a small syntax thing that I'm missing. Any ideas?
You're dereferencing the pointer so you're passing a const char, not a const char*.
try this:
generate_vectorlist(outfile_name);
You have two problems:
generate_vectorlist takes a const char *, not a const char &.
The template type is not in the function signature so the compiler can not deduce the type therefore you need to specify it (using int in my example).
So you need to do:
generate_vectorlist<int>(outfile_name);
In the first bit, try:
generate_vectorlist(outfile_name);
You should be passing a character pointer, not a character.
You need to specify the template argument. There's nothing the compiler can use to deduce what type T is. So you'll have to call it like generate_vectorlist<MyType>(outfile_name);, using the appropriate type for MyType.
This is the problem:
template <class T>
void generate_vectorlist(const char* outfile_name = "input.txt" )
The compiler can't deduce the type of T, so it has no idea which generate_vectorlist to use.
Call it like this:
generate_vectorlist<vectortype>(outfile_name);
Though I would actually suggest that this code doesn't make any sense in the first place.