I am trying to understand template specialization in C++. I have read other forums, but cannot get it working in practice. I am trying to learn with a very simple example which I will explain.
What I would like to accomplish: I want foo to exhibit different behaviors based on the type. The code below does not work, but I have commented the behavior I would like to see. Could someone please fill in the lines that I commented. Please let me know if anything is unclear.
#include <iostream>
#include <string>
template <typename T>
class my_template
{
public:
foo() {return 0} // default behavior if there does not exist foo() function for the specified type
};
template <>
class my_template<int>
{
public:
// implement foo function: should return -1 if the type = int
};
template <>
class my_template<long>
{
public:
// implement foo function: should return 100 if the type = long
};
int main()
{
my_template<int> x;
my_template<long> y;
my_template<double> z;
std::cout << x.foo() << "\n"; // print -1
std::cout << y.foo() << "\n"; // print 100
std::cout << z.foo() << "\n"; // print 0
return 0;
}
Just to show you a few different approaches.
If you use a metafunction approach, then nothing will ever be done at run-time (guaranteed):
template<typename>
struct my_template{
enum { value = 0 };
};
template<>
struct my_template<int>{
enum { value = -1 };
};
template<>
struct my_template<long>{
enum { value = 100 };
};
int main(){
std::cout << "float: " << my_template<float>::value << '\n';
std::cout << "int: " << my_template<int>::value << '\n';
std::cout << "long: " << my_template<long>::value << '\n';
}
Or you could use a template variable ( C++14 ):
template<typename>
constexpr int my_value = 0;
template<>
constexpr int my_value<int> = -1;
template<>
constexpr int my_value<long> = 100;
int main(){
std::cout << "float: " << my_value<float> << '\n';
std::cout << "int: " << my_value<int> << '\n';
std::cout << "long: " << my_value<long> << '\n';
}
Or use a template function:
template<typename T>
int func_impl(T){ return 0; }
int func_impl(int){ return -1; }
int func_impl(long){ return 100; }
template<typename T>
int func(){
return func_impl(T(0));
}
int main(){
std::cout << "float: " << func<float>() << '\n';
std::cout << "int: " << func<int>() << '\n';
std::cout << "long: " << func<long>() << '\n';
}
template <typename T>
class my_template
{
public:
int foo() {return 0;} // default behavior
};
template <>
class my_template<int>
{
public:
int foo() {return -1;}
};
Is that enough?
Related
I want to know if it's possible to have the same function but for different type like in c++ for Typescript ?
#include <iostream>
template <typename T>
void print(double x)
{
std::cout << "IS DOUBLE: " << x << std::endl;
}
template <typename T>
void print(int x)
{
std::cout << "IS INT: " << x << std::endl;
}
template <typename T>
void print(std::string x)
{
std::cout << "IS STRING: " << x << std::endl;
}
int main()
{
print<int>(2);
print<double>(8.0);
print<std::string>("HA");
return 0;
}
There is the output
IS INT: 2
IS DOUBLE: 8
IS STRING: HA
This question already has answers here:
What is object slicing?
(18 answers)
Closed 1 year ago.
I am building up a CRTP interface and noticed some undefined behavior. So, I built up some sample code to narrow down the problem.
#include <iostream>
template <typename T>
class Base {
public:
int a() const { return static_cast<T const&>(*this).a_IMPL(); }
int b() const { return static_cast<T const&>(*this).b_IMPL(); }
int c() const { return static_cast<T const&>(*this).c_IMPL(); }
};
class A : public Base<A> {
public:
A(int a, int b, int c) : _a(a), _b(b), _c(c) {}
int a_IMPL() const { return _a; }
int b_IMPL() const { return _b; }
int c_IMPL() const { return _c; }
private:
int _a;
int _b;
int _c;
};
template <typename T>
void foo(const T& v) {
std::cout << "foo()" << std::endl;
std::cout << "a() = " << static_cast<Base<T>>(v).a() << std::endl;
std::cout << "b() = " << static_cast<Base<T>>(v).b() << std::endl;
std::cout << "c() = " << static_cast<Base<T>>(v).c() << std::endl;
}
int main() {
A v(10, 20, 30);
std::cout << "a() = " << v.a() << std::endl;
std::cout << "b() = " << v.b() << std::endl;
std::cout << "c() = " << v.c() << std::endl;
foo(v);
return 0;
}
The output of this code is:
a() = 10
b() = 20
c() = 30
foo()
a() = 134217855
b() = 0
c() = -917692416
It appears that there is some problem when casting the child class, which implements the CRTP "interface", to the interface itself. This doesn't make sense to me because the class A plainly inherits from Base so, shouldn't I be able to cast an instance of A into Base?
Thanks!
You copy and slice when you cast to Base<T>.
Cast to a const Base<T>& instead:
std::cout << "a() = " << static_cast<const Base<T>&>(v).a() << std::endl;
std::cout << "b() = " << static_cast<const Base<T>&>(v).b() << std::endl;
std::cout << "c() = " << static_cast<const Base<T>&>(v).c() << std::endl;
It turns out I was casting incorrectly to a value rather than a reference
std::cout << "a() = " << static_cast<Base<T>>(v).a() << std::endl;
should become
std::cout << "a() = " << static_cast<const Base<T>&>(v).a() << std::endl;
I'm trying to dispatch my calls in 2 different functions. One for pointers and the other for references. But as soon as I use the const qualifier, templates doesn't dispatch as expected. In my case, the get_pixel doesn't use any const qualifier because it is supposed to edit the given parameter. And set_pixel is supposed to use the given parameter but don't edit it and I would like those parameters to remain const.
#include <iostream>
template <typename Color>
inline int get_pixel(
Color& color)
{
return 1;
}
template <typename T>
inline int get_pixel(
T components[])
{
return 2;
}
template <typename Color>
inline int set_pixel(
const Color& color)
{
return 1;
}
template <typename T>
inline int set_pixel(
const T components[])
{
return 2;
}
template <typename Color>
inline int set_pixel_no_const(
Color& color)
{
return 1;
}
template <typename T>
inline int set_pixel_no_const(
T components[])
{
return 2;
}
int main()
{
float c;
float tab[1];
std::cout << "Get PIXEL\n";
std::cout << "Dispatch for c : " << get_pixel(c) << "\n"; // 1
std::cout << "Dispatch for &c : " << get_pixel(&c) << "\n"; // 2
std::cout << "Dispatch for tab : " << get_pixel(tab) << "\n"; // 2
std::cout << "Set PIXEL\n";
std::cout << "Dispatch for c : " << set_pixel(c) << "\n"; // 1
std::cout << "Dispatch for &c : " << set_pixel(&c) << "\n"; // 1, Should be 2
std::cout << "Dispatch for tab : " << set_pixel(tab) << "\n"; // 1, Should be 2
std::cout << "Set PIXEL NO CONST\n";
std::cout << "Dispatch for c : " << set_pixel_no_const(c) << "\n"; // 1
std::cout << "Dispatch for &c : " << set_pixel_no_const(&c) << "\n"; // 2
std::cout << "Dispatch for tab : " << set_pixel_no_const(tab) << "\n"; // 2
return 0;
}
Any idea why the const qualifier is a problem here ?
The template deduction doesn't work as a text substitution, but on the T as a whole.
When T in const T is deduced as float* you don't get const float*, but float* const.
Or const (float*), if we had such a syntax.
I may be wrong, but i think basically this question boils down to this:
#include <iostream>
template <typename T>
int f(T const &)
{
//std::cout << __PRETTY_FUNCTION__ << "\n";
return 1;
}
template <typename T>
int f(T const *)
{
//std::cout << __PRETTY_FUNCTION__ << "\n";
return 2;
}
int main()
{
float c = 1.f;
float * addr = &c;
float const * addr_const = &c;
f(c); // 1
f(&c); // 1 you expected 2
f(addr); // 1 you expected 2
f(addr_const); // 2 as you expect
return 0;
}
Your const array function parameter is the same as a const pointer parameter in the function declaration(so i put it in this way in the example).
I think the first function is the base template, while the second version is a more specialized version (since it only takes pointers to const T). So the const reference one gets chosen when you pass a pointer to non const. Except in the case you really pass it a pointer to const as argument.
If you use gcc (i think) you can use __PRETTY_FUNCTION__ to display the deduced arguments
Suppose I have some structs and each of them holds one enum as a member.
I want to call a method of a struct but depending on a struct's member, like in the code example:
#include <iostream>
#include <string>
#include <type_traits>
enum class Type{
lowercase = 0,
uppercase
};
struct Low{
static constexpr Type cp = Type::lowercase;
};
struct Up{
static constexpr Type cp = Type::uppercase;
};
template<typename Case, typename=void>
struct Printer
{
void print_g(const std::string& s){
std::cout << "just s: " << s << std::endl;
}
};
template<typename X>
struct Printer<X, std::enable_if_t<X::cp == Type::lowercase, void>>
{
void print_g(const std::string& s){
std::cout << "lowercase " << std::nouppercase << s << std::endl;
}
};
template<typename X>
struct Printer <X, std::enable_if_t<X::cp == Type::uppercase, void>>
{
void print_g(const std::string& s){
std::cout << "uppercase " << std::uppercase << s << std::endl;
}
};
int main()
{
Printer<Low> pl;
pl.print_g("hello1");
Printer<Up> p2;
p2.print_g("hello2");
}
But this solution doesn't look quite elegant to me.
Especially the part typname=void in the first template.
Only then code compiles. Why is that the case?
And is there any better (more elegant) solution for this template specialization?
In C++17, you can use if constexpr:
template <typename X>
struct Printer
{
void print_g(const std::string& s)
{
if constexpr(X::cp == Type::lowercase)
{
std::cout << "lowercase " << std::nouppercase << s << std::endl;
}
else if constexpr(X::cp == Type::uppercase)
{
std::cout << "uppercase " << std::uppercase << s << std::endl;
}
else
{
std::cout << "just s: " << s << std::endl;
}
}
};
If you do not have access to C++17, consider these options:
Use a regular if...else statement. There's no code that needs to be conditionally compiled in your example.
Implement static_if in C++14. Here's a talk I gave that explains how to do it: Implementing static control flow in C++14
You can fully specialize Printer for Low and Up.
template<class Case>
struct Printer
{
void print_g(const std::string& s) {
std::cout << "just s: " << s << std::endl;
}
};
template<>
struct Printer<Low>
{
void print_g(const std::string& s) {
std::cout << "lowercase " << std::nouppercase << s << std::endl;
}
};
template<>
struct Printer<Up>
{
void print_g(const std::string& s) {
std::cout << "uppercase " << std::uppercase << s << std::endl;
}
};
Notice that the enum does not come into play at all. If you need to specialize for the enum, you can do that too.
template<Type Case>
struct PrinterHelper
{
void print_g(const std::string& s) {
std::cout << "just s: " << s << std::endl;
}
};
template<>
struct PrinterHelper<Type::lowercase>
{
void print_g(const std::string& s) {
std::cout << "lowercase " << std::nouppercase << s << std::endl;
}
};
template<>
struct PrinterHelper<Type::uppercase>
{
void print_g(const std::string& s) {
std::cout << "uppercase " << std::uppercase << s << std::endl;
}
};
template<class Case>
using Printer = PrinterHelper<Case::cp>;
I would likely just go with:
enum struct Casing
{
Lower,
Upper
};
template<Casing>
struct printer;
template<>
struct printer<Casing::Lower>
{
...
};
template<>
struct printer<Casing::Upper>
{
...
};
Why b.isEm() prints different things on different lines when I have not changed anything after the last call of b.isEm()?
#include <iostream>
#include <string>
template <class T>
class Box
{
bool m_i;
T m_c;
public:
bool isEm() const;
void put(const T& c);
T get();
};
template <class T>
bool Box<T>::isEm() const
{
return m_i;
}
template <class T>
void Box<T>::put(const T& c)
{
m_i = false;
m_c = c;
}
template <class T>
T Box<T>::get()
{
m_i = true;
return T();
}
int main()
{
Box<int> b;
b.put(10);
std::cout << b.get() << " " << b.isEm() << std::endl;
std::cout << b.isEm() << std::endl;
}
The order of evaluation of function arguments in C++ is unspecified.
std::cout << b.get() << " " << b.isEm() << std::endl;
std::cout << b.isEm() << std::endl;
Since b.get() has side effects, I suggest you call it separately...
auto g = b.get();
std::cout << g << " " << b.isEm() << std::endl;
std::cout << b.isEm() << std::endl;
Note: std::cout << .... << ... << is a function call with the arguments ...