I have a struct and template class, within which there is a function that should check if T is equal to the struct and if so, do something.
The struct:
struct mystruct
{
int x;
int y;
int z;
};
the template class:
template <typename T>
class myclass
{
public:
void myfunc()
{
// this condition is ignored..
if(std::is_same<T,mystruct>::value==0)
{
cout << "T is not mystruct type" << '\n';
}
else
{
T ms;
ms.x = 5;
ms.y = 4;
ms.z = 3;
}
}
};
in main function, if T == mystruct everything goes through fine:
int main()
{
// no issues
myclass<mystruct> x;
x.myfunc();
}
but if T != mystruct:
int main()
{
//tries to unsuccessfuly convert int to mystruct
myclass<int> x;
x.myfunc();
}
execution fails with the below error:
error: request for member 'x' in 'ms', which is of non-class type 'int'
ms.x = 5;
does anyone have an idea why the if-else statement not working as expected?
Thanks!
Even if the if condition evaluates to false, for a particular template instantiation, the entire template still must consist of valid C++ code.
If the template's parameter is int, for example, then the else part of the if statement becomes equivalent to:
else
{
int ms;
ms.x = 5;
ms.y = 4;
ms.z = 3;
}
It should be obvious why this won't compile. Because the entire template becomes, equivalent to:
if (true)
{
cout << "T is not mystruct type" << '\n';
}
else
{
int ms;
ms.x = 5;
ms.y = 4;
ms.z = 3;
}
Even though else never gets executed, it stlil must be valid C++ code. Templates are no different.
C++17 introduced if constexpr which requires that the evaluated if expression is constant, and only the appropriate part of the if statement ends up being compiled, with the rest being effectively discarded. So, with C++17, you should be able to change the if statement to if constexpr, and get the expected results.
Related
I have some generic function, which looks similar to
template<int P>
struct foo {
static const int value = ...;
};
Now, I want to call this generic function in a loop. Like so:
for (int i = 0; i < 100; ++i)
{
auto bar = foo<i>::value;
}
But I get a lot of error messages. Like
the value of 'i' is not used in a constant expression
int i is not constant
I tried to fix it with:
foo<(const int)i>::value;
But to no avail. So, what is wrong with that and how can I make it work?
You cannot do it this way.
for (int i = 0; i < 100; ++i)
{
auto bar = foo<i>::value;
}
i needs to me a constant expression, so that the compiler can generate the code for it when it compiles your program.
Here's an exhaustive explanation of what constant expressions are:
http://en.cppreference.com/w/cpp/language/constant_expression
Here's a segment from the site:
int n = 1;
std::array<int, n> a1; // error: n is not a constant expression
const int cn = 2;
std::array<int, cn> a2; // OK: cn is a constant expression
So to make it happen compile time, you need to make your loop into a template recursion using variadic templates.
Maybe you can understand what you need to do better if you read this example:
// Example program
#include <iostream>
#include <string>
template<int P>
struct foo
{
static const int value = P;
};
template <int TIndex>
int call()
{
return foo<TIndex>::value;
}
template <int TIndex, int TIndex2, int ...Rest>
int call ()
{
return call<TIndex>() + call<TIndex2, Rest...>();
}
int main()
{
std::cout << "Test: " << call<1, 2>() << "\n"; // prints "Test: 3"
}
Nicky C posted a link to another question. It has a good answer and I don't feel right in copying it here. Take a look at my working example above and then look at the answer here:
https://stackoverflow.com/a/11081785/493298
You should be able to make it work. It's a bit of a syntax hell, but you can manage it, I'm sure.
I am trying to design a class which all its data is constant and know at compile time. I could just create this by manually typing it all but I want to use a template so that I don't have to rewrite almost the same code many times.
I was thinking templates are the way to do this e.g
template<class T> class A { ... }
A<float>
A<MyObject>
A<int>
But then I wasn't sure how I could get the constant data that I know into this object. I could do it at run-time with a member function which does a switch statement on the type or something similar but I ideally want it to effectively be a dumb data holder for me to use.
So in the case of A<float> I would have this:
// member function
int getSize() {
return 4;
}
Instead of (pseudo code)
// member function
int getSize() {
if (type == float) {
return 4;
} else if ...
}
I'm wondering if there is a known way to do this? I don't have any experience with constexpr, could that be the key to this?
edit: To clarify: I want member functions which always return the same result based on the templated type/class. For example, A would always return 4 from getSize() and 1 from getSomethingElse() and 6.2 from getAnotherThing(). Where as A would return 8 from getSize() and 2 from getSomethingElse() and 8.4 from getAnotherThing().
You can have this template
template <int size_, int foo_, int bar_>
struct MyConstData {
static const int size = size_; // etc
};
Then specialize your template:
template <class T> class A;
template <> class A<float> : MyConstData<13,42,-1> {};
template <> class A<double> : MyConstData<0,0,42> {};
You can specialize particular functions within a class, and given your description of things, I suspect that's what you want. Here is an example of how this works:
#include <iostream>
#include <string>
template <class T>
class A {
public:
int MyConstantFunction() const { // Default implementation
return 0;
}
};
template <>
int A<int>::MyConstantFunction() const
{
return 3;
}
template <>
int A<float>::MyConstantFunction() const
{
return 5; // If you examine the world, you'll find that 5's are everywhere.
}
template <>
int A<double>::MyConstantFunction() const
{
return -5;
}
int main(int, char *[])
{
using ::std::cout;
A<int> aint;
A<float> afloat;
A<long> along;
cout << "aint.MyConstantFunction() == " << aint.MyConstantFunction() << '\n';
cout << "afloat.MyConstantFunction() == "
<< afloat.MyConstantFunction() << '\n';
cout << "along.MyConstantFunction() == "
<< along.MyConstantFunction() << '\n';
return 0;
}
Notice how along just used the default implementation from the class declaration. And this highlights a danger here. If the translation unit using your specialization for a given type hasn't seen that specialization, it won't use it, and that may cause all kinds of interesting problems. Make sure this happens.
The other option is to not provide a default implementation at all, and so you get an instantiation error.
My gut feeling is that you are doing something that is pointless and a poor design. But, since I don't know the full context I can't say that for sure. If you insist on doing this, here's how.
If you want to implement different things depending on the type, you could try this:
template <class T>
class Foo {
T data;
string toString() {
return myGeneralToString(data);
}
};
template <>
class Foo<string> {
string data;
string toString() {
return "Already a string: " + data;
}
};
If you just want templated constants, I'd try this:
template <int a, int b>
class Calc {
public:
static constexpr int SUM = a + b;
};
int main()
{
std::cout << Calc<3, 5>::SUM << std::endl;
return 0;
}
Edit: as pointed out by Omnifarious C++14 has templated constants without templating the class itself. So you could simplify the example to:
class Calc {
public:
template <int a, int b>
static constexpr int SUM = a + b;
};
int main()
{
std::cout << Calc::SUM<3, 5> << std::endl;
return 0;
}
Consider I have a class template which takes an array as an argument:
template <int N, const float numbers[N]>
class TemplateClass
{
public:
TemplateClass()
{
for (int i = 0; i < N; i++)
{
cout << numbers[i] << endl;
}
}
};
I can use it successfully like this:
const int N = 3;
extern const float Constants[N];
const float Constants[N] = { 2.0f, 2.1f, 2.2f };
int main()
{
TemplateClass<3, Constants>();
return 0;
}
However, my attempts at moving the constructor method body outside of the class declaration were in vain:
// Fails with:
// error C2440: 'specialization' : cannot convert from 'const float *' to 'const float [3]'
template <int N, const float numbers[N]>
TemplateClass<N, numbers>::TemplateClass()
{
for (int i = 0; i < N; i++)
{
cout << numbers[i] << endl;
}
}
// Fails with:
// error C3860: template argument list following class template name must list parameters in the order used in template parameter list
// error C3855: 'TemplateClass<N,numbers>': template parameter 'numbers' is incompatible with the declaration
template <int N, const float* numbers>
TemplateClass<N, numbers>::TemplateClass()
{
for (int i = 0; i < N; i++)
{
cout << numbers[i] << endl;
}
}
This is observed on both VC++11 and VC++12 compilers. How do I solve this?
Reason/Bug:
According to the standard §14.1/4, non-type template-parameter must have one of the following types:
Integral or enumeration type,
Pointer to object or pointer to function,
lvalue reference to object or lvalue reference to function,
pointer to member,
std::nullptr_t.
const float numbers[N] is none of the above. See also (https://stackoverflow.com/a/16214727/2352671)
Now the question that still remains is why when you define the constructor in-line (i.e., inside class's definition) your program compiles and runs fine.
The answer to this question is that Non-type template parameters that are declared as arrays or functions are converted to pointers or pointers to functions, respectively. We can confirm this by printing the type
of numbers inside the in-lined constructor:
template <int N, const float numbers[N]>
class TemplateClass
{
public:
TemplateClass()
{
std::cout << typeid(numbers).name() << std::endl;
for (int i = 0; i < N; i++)
{
std::cout << numbers[i] << std::endl;
}
}
};
The output we are getting is:
float const *
As such, the standards aren't being violated and the code works.
The next question we have to answer is why when you define the constructor outside class's definition with the template list of parameters as template <int N, const float *numbers>
you get a compiler error.
The answer to this question is because the list of parameters in you class definition (i.e., template <int N, const float numbers[N]>) and the list of parameters in your construction's definition do not match.
Solution:
#include <iostream>
using namespace std;
template <int N, const float* numbers>
class TemplateClass
{
public:
TemplateClass();
};
template <int N, const float *numbers>
TemplateClass<N, numbers>::TemplateClass()
{
for (int i = 0; i < N; i++)
{
cout << numbers[i] << endl;
}
}
const int N = 3;
extern const float Constants[N];
const float Constants[3] = { 2.0f, 2.1f, 2.2f };
int main()
{
TemplateClass<3, Constants>();
return 0;
}
Output:
2
2.1
2.2
the template methods must be defined in the same file header of the template class. A template is a declaration only, It is instantiated by compiler when you use it for your variable, so the template methods cannot be implemented in a cpp file.
Consider the following code C++:
#include<iostream>
using namespace std;
class Test {
int &t;
public:
Test (int &x) { t = x; }
int getT() { return t; }
};
int main()
{
int x = 20;
Test t1(x);
cout << t1.getT() << " ";
x = 30;
cout << t1.getT() << endl;
return 0;
}
It is showing the following error while using gcc compiler
est.cpp: In constructor ‘Test::Test(int&)’:
est.cpp:8:5: error: uninitialized reference member ‘Test::t’ [-fpermissive]
Why doesn't the compiler directly call the Constructor?
That is because references can only be initialized in the initializer list. Use
Test (int &x) : t(x) {}
To explain: The reference can only be set once, the place where this happens is the initializer list. After that is done, you can not set the reference, but only assign values to the referenced instance. Your code means, you tried to assign something to a referenced instance but the reference was never initialized, hence it's not referencing any instance of int and you get the error.
My compiler emits this error:
error C2758: 'Test::t' : must be initialized in constructor base/member initializer list
And that's exactly what you must do. References must be initialized in the initializer list:
#include<iostream>
using namespace std;
class Test {
int &t;
public:
Test (int &x) : t(x) { } // <-- initializer list used, empty body now
int getT() { return t; }
};
int main()
{
int x = 20;
Test t1(x);
cout << t1.getT() << " ";
x = 30;
cout << t1.getT() << endl;
return 0;
}
Explanation:
If the reference is not in the initiliazer list, it's next to impossible for the compiler to detect if the reference is initialized. References must be initialized. Imagine this scenario:
Test (int &x, bool b)
{
if( b ) t = x;
}
Now it would be up to the caller of the constructor to decide if valid code was generated. That cannot be. The compiler must make sure the reference is initialized at compile time.
In the code below:
template<typename T>
struct X {};
int main()
{
X<int()> x; // what is the type of T ?
}
What is the type of T? I saw something like this in the boost sources.
Consider the function int func(). It has a function type int(void). It can be implicitly converted to pointer type as the C++ Standard says in 4.3/1, but it this case there's no need in such conversion, so T has the function type int(void), not a pointer to it.
Here is what I did. Though the output of code below is implementation specific, many times it gives a good hint into the type of T that we are dealing with.
template<typename T>
struct X {
X(){
cout << typeid(T).name();
}
};
int main()
{
X<int()> x; // what is the type of T ?
cout << typeid(int()).name() << endl;
}
The output on VC++ is
int __cdecl(void)
int __cdecl(void)
The type of T is a function that takes no parameters and returns int, as in:
template<typename T>
struct X {};
int foo()
{
return 42;
}
int main()
{
X<int()> x; // what is the type of T ?
typedef int(foo_ptr)();
X<foo_ptr> x2;
return 0;
}
T in x and x2 are the same type.