Based on this SO answer, I was experimenting with something similar but with a pointer:
#include <iostream>
class Bar {
public:
virtual ~Bar() {}
};
class Foo: Bar {
public:
Foo() { std::cout << "Foo::Foo()" << std::endl; }
~Foo() override { std::cout << "Foo::~Foo()" << std::endl; }
};
class Faz {
public:
Faz() { std::cout << "Faz::Faz()" << std::endl; }
~Faz() { std::cout << "Faz::~Faz()" << std::endl; }
};
template <typename T>
typename std::enable_if<std::is_base_of<Bar, std::remove_pointer<T>>::value>::type
func(char const* type, T) {
std::cout << type << " is derived from Bar" << std::endl;
}
template <typename T>
typename std::enable_if<!std::is_base_of<Bar, std::remove_pointer<T>>::value>::type
func(char const* type, T) {
std::cout << type << " is NOT derived from Bar" << std::endl;
}
int main()
{
func("std::unique_ptr<Foo>", std::unique_ptr<Foo>());
func("std::unique_ptr<Faz>", std::unique_ptr<Faz>());
}
cout is :
std::unique_ptr<Foo> is NOT derived from Bar
std::unique_ptr<Faz> is NOT derived from Bar
Why does !std::is_base_of<Bar, type_identity<std::remove_pointer<T>>>::value always evaluate as true? I assumed (as a beginner):
std::unique_ptr<Foo> is derived from Bar
std::unique_ptr<Faz> is NOT derived from Bar
I'm probably missing something stupid.
std::remove_pointer<> acts on raw pointer types.
The smart pointer's type is not related to the class hierarchy of referent type
Related
Edit: with final keyword on the implementations of the virtual function results in printing the correct string, but why is the final keyword here needed? Could somebody explain?
I am tinkering with variadic templates, I have pretty generic classes D1, D2, D3, ... and they all derive from a class A. Each class has a static and a dynamic print functions, the parent class has a virtual dynamic print function for dynamic dispatching. When I try to reproduce it on a single file:
class A {
public:
virtual void printDynamic();
static void printStatic();
}
class D1 : public A {
public:
virtual void printDynamic();
static void printStatic();
}
And I have following variants:
std::variant<A,As...> apvar;
std::variant<A*,As*...> avar;
I instantiate both variants with all the derived classes D1,D2,... (I know upcasting Pointers i just want to dereference to their types and do random stuff)
I have implemented recursive visitors for the wrappers, i need to capture this because I encapsulated most of the functions in a class, when I call the visitor on classes I get the names "DX", "DX" ; X corresponding to 1.
template<class X, class Y, class... Zs>
void visit_actor(){
std::visit(
[this](auto&& value){
if constexpr(std::is_same<expr_type<decltype(value)>,expr_type<X>>::value){
value.printStaticName();
value.printDynamicName();
} else{
visit_actor<Y,Zs...>();
}
}, avar
);
}
But if I call the visitor on pointer variants and when I call the functions:
For the static function I get: "DX" with X corresponding to I, but when I call the dynamic function I get the name: "Abstract A".
template<class X, class Y, class... Zs>
void visit_pointer(){
std::visit(
[this](auto&& value){
if constexpr(std::is_same<expr_type<decltype(value)>,expr_type<X>>::value){
value->printStaticName();
value->printDynamicName();
} else{
visit_pointer<Y,Zs...>();
}
}, apvar
);
}
I have tried reading about it in c++ documentation but could not find the reason yet. What could be the reason that the static function of the derived class is called but the parents virtual function is called ?
For A Minimal Producable example, you need to instantiate the classes:
#include <variant>
#include <iostream>
#include <string>
template <class T>
using expr_type = std::remove_cv_t<std::remove_reference_t<T>>;
template<class A,class... As>
class ActorWrapper{
public:
std::variant<A,As...> var;
template<class Ins>
ActorWrapper(Ins ins) : var(ins) {}
};
template<class A,class... As>
class ActorPointer{
public:
std::variant<A,As... > var;
template<class T>
ActorPointer(T* t) : var(t) {}
};
class X {
public:
int a;
virtual std::string getDynamicName() {
return "dynamic X";
}
static std::string getStaticName(){
return "static X";
}
};
class D1 : public X{
public:
bool b;
std::string getDynamicName() override {
return "dynamic D1";
}
static std::string getStaticName(){
return "static D1";
}
};
class D2: public X {
public:
bool b;
std::string getDynamicName() override {
return "dynamic D2";
}
static std::string getStaticName(){
return "static D2";
}
};
template<class A, class... As>
class TemplatedAGraph{
private:
//change aw to correspond to ap
template<class X>
void visit_actor(){
std::visit(
[this](auto&& value){
if constexpr(std::is_same<expr_type<decltype(value)>, expr_type<X>>::value){
std::cout << "z" << std::endl;
std::cout << value.getStaticName() << std::endl;
std::cout << value.getDynamicName() << std::endl;
}else{
std::cout << "d" << std::endl;
return;
}
}, aw.var
);
}
template<class X, class Y, class... Zs>
void visit_actor(){
std::visit(
[this](auto&& value){
if constexpr(std::is_same<expr_type<decltype(value)>, expr_type<X>>::value){
std::cout << "x" << std::endl;
std::cout << value.getStaticName() << std::endl;
//std::cout << value.getDynamicName() << std::endl;
} else{
std::cout << "y" << std::endl;
visit_actor<Y,Zs...>();
}
}, aw.var
);
}
template<class X>
void visit_pointer(){
std::visit(
[this](auto&& value){
if constexpr(std::is_same<expr_type<decltype(value)>, expr_type<X>>::value){
std::cout << "a" << std::endl;
std::cout << value->getStaticName() << std::endl;
std::cout << value->getDynamicName() << std::endl;
}else{
std::cout << "b" << std::endl;
return;
}
}, ap.var
);
}
template<class X, class Y, class... Zs>
void visit_pointer(){
std::visit(
[this](auto&& value){
if constexpr(std::is_same<expr_type<decltype(value)>, expr_type<X>>::value){
std::cout << "c" << std::endl;
std::cout << value->getStaticName() << std::endl;
std::cout << value->getDynamicName() <<std::endl;
} else{
//std::cout << typeid(decltype(value)).name() <<std::endl;
//std::cout << typeid(X).name() <<std::endl;
//std::cout << std::is_same_v<decltype(value),X> << std::endl;
std::cout << "d" << std::endl;
visit_pointer<Y,Zs...>();
}
}, ap.var
);
}
public:
ActorPointer<A*,As*...> ap;
ActorWrapper<A,As...> aw;
void print_names(){
visit_actor<A,As...>();
}
void print_names_w_pointer(){
visit_pointer<A*,As*...>();
}
//change ap to coresspond to aw
template<class X>
TemplatedAGraph(X a) : ap(&a), aw(a) {}
};
int main(){
D2 d2;
D2* d2ref = &d2;
std::cout << d2ref->getDynamicName() << std::endl;
TemplatedAGraph<D1,D2> tag(d2);
tag.print_names();
tag.print_names_w_pointer();
}
The Output is:
thrud#thrud ~/wörk/test $ g++ main.cpp -std=c++17
thrud#thrud ~/wörk/test $ ./a.out
dynamic D2
y
z
static D2
dynamic D2
d
a
static D2
Segmentation fault
So I think I have stumbled upon an undefined behaviour, but still I would like to know the reason.
TemplatedAGraph(X a) : ap(&a), aw(a) {} stores a pointer to a local variable in ap. That pointer becomes dangling soon afterwards. Any attempt to access it then exhibits undefined behavior.
You might have meant TemplatedAGraph(X& a) :.... This way, your code works, as far as I can tell.
This code:
#include <iostream>
class A {};
class B : public A {};
class C {
public:
template <typename T>
void x(const T& t) {
std::cout << "template" << std::endl;
}
void x(const A*& a) {
std::cout << "a" << std::endl;
}
void x(const int& a) {
std::cout << "int" << std::endl;
}
template <typename T>
void y(T t) {
std::cout << "template" << std::endl;
}
void y(A* a) {
std::cout << "a" << std::endl;
}
void y(int a) {
std::cout << "int" << std::endl;
}
template <typename T>
void z(const T& t) {
std::cout << "template" << std::endl;
}
};
// Does not compile
// template <>
// void C::z(const A*& a) {
// std::cout << "a" << std::endl;
// }
template <>
void C::z(const int& a) {
std::cout << "int" << std::endl;
}
int main(int argc, char** argv) {
C c;
c.x(new A());
c.x(new B());
c.x(1);
c.y(new A());
c.y(new B());
c.y(1);
c.z(new A());
c.z(new B());
c.z(1);
}
Prints:
template
template
int
a
template
int
template
template
int
I have the following questions about that:
Why does void C::z(const int& a) compile but void C::z(const A*& a) does not?
What is a reasonable solution to problem? I need to have a templated function for generically handling a wide variety of arguments, but a large set of classes with a common base needs to be handled specifically. I need some approach that will print a a int.
Edit: Thanks to the suggestion of #AndyG I was able to resolve this with some type_traits and the code below:
#include <boost/utility/enable_if.hpp>
#include <boost/type_traits/is_base_of.hpp>
#include <boost/type_traits/remove_pointer.hpp>
#include <iostream>
class A {};
class B : public A {};
class C {
public:
template <typename T>
typename boost::disable_if<boost::is_base_of<A, typename boost::remove_pointer<T>::type>, void>::type x(const T& t) {
std::cout << "template" << std::endl;
}
void x(A*const& a) {
std::cout << "a" << std::endl;
}
void x(const int& a) {
std::cout << "int" << std::endl;
}
};
int main(int argc, char** argv) {
C c;
c.x(new A());
c.x(new B());
c.x(1);
}
The answer is because a const on a pointer type is a little weird.
What you want is this:
template <>
void C::z( A*const& a) {
std::cout << "a" << std::endl;
}
const needs to be read right to left. Since z accepts a T&, when you want to specialize for A* you need to place the const after A* instead of in front.
Demo
I have a templated class pair, and I'd like to write a show function outside of the class to do some fancy couting. When specifying the template type in show explictly, it all works as expected:
#include <iostream>
template <class A_Type>
class pair
{
public:
A_Type a0;
A_Type a1;
};
void show(const pair<double> & p) {
std::cout << p.a0 << std::endl;
std::cout << p.a1 << std::endl;
}
int main() {
pair<double> p;
p.a0 = 1.2;
p.a1 = 1.3;
show(p);
}
I'd like to have show oblivious of the template type though.
Any hints?
You can change show function to:
template<typename DataType>
void show(const pair<DataType> & p) {
std::cout << p.a0 << std::endl;
std::cout << p.a1 << std::endl;
}
Or a better approach (in my opinion) is to make show function member of the class:
template <class A_Type>
class pair {
public:
A_Type a0;
A_Type a1;
void show() const {
std::cout << this->a0 << std::endl;
std::cout << this->a1 << std::endl;
}
};
and then simply:
int main() {
pair<double> p;
p.a0 = 1.2;
p.a1 = 1.3;
p.show();
}
from the code below, I get the following output:
Call member_function
Derived member function
Call template_function
template function
Derived member function
As expected, the specialization of template_function is not called here because derived is of type Base*, but the correct version of member_function is called.
However, sometimes, it may be useful to call not-member functions in template functions.
Does exist a way to ensure that the specialized version of the template function will be called when dynamic instances of Derived class are declared as being of type Base*?
Thanks!
#include <iostream>
// Base and Derived classes
class Base
{
public:
virtual void member_function() const
{ std::cout << "Base member function" << std::endl; };
};
class Derived : public Base
{
public:
virtual void member_function() const
{ std::cout << "Derived member function" << std::endl;};
};
// Functions
template<typename T>
void template_function(T const & arg)
{
std::cout << "template function" << std::endl;
arg.member_function();
}
template<>
void template_function(Derived const & arg)
{
std::cout << "Specialized function" << std::endl;
arg.member_function();
}
// Main
int main ()
{
Base * derived = new Derived();;
std::cout << "Call member_function" << std::endl;
derived->member_function();
std::cout << std::endl;
std::cout << "Call template_function" << std::endl;
template_function(*derived);
}
You can add two templates template_function that you enable_if on std::base_of<T, Derived>, like this
// Functions
template<typename T, std::enable_if_t<not std::is_base_of<T, Derived>::value>* = nullptr>
void template_function(T const & arg)
{
std::cout << "template function" << std::endl;
arg.member_function();
}
template<typename T, std::enable_if_t<std::is_base_of<T, Derived>::value>* = nullptr>
void template_function(T const & arg)
{
std::cout << "Specialized function" << std::endl;
arg.member_function();
}
// Main
int main ()
{
Base const * base = new Base();
Base const * derived = new Derived();
std::cout << "Call member_function" << std::endl;
base->member_function();
derived->member_function();
std::cout << std::endl;
std::cout << "Call template_function" << std::endl;
template_function(*base);
template_function(*derived);
}
Live Example.
Alternatively, and much simpler, you can simply add a template_function(Base const&) overload
// Functions
template<typename T>
void template_function(T const & arg)
{
std::cout << "template function" << std::endl;
arg.member_function();
}
void template_function(Base const & arg)
{
std::cout << "Specialized function" << std::endl;
arg.member_function();
}
Live Example
I have this code:
#include <iostream>
#include <string>
class Foo {
public:
Foo(){};
template<typename T>
Foo (T&) {
std::cout << "template" << std::endl;
}
Foo(Foo&) {
std::cout << "copy" << std::endl;
}
Foo(const Foo&) {
std::cout << "copy2" << std::endl;
}
};
int main(){
Foo f;
Foo f2 (f);
}
It prints "copy", which is correct.
However if I remove the Foo(Foo&):
class Foo {
public:
Foo(){};
template<typename T>
Foo (T&) {
std::cout << "template" << std::endl;
}
//Foo(Foo&) {
// std::cout << "copy" << std::endl;
//}
Foo(const Foo&) {
std::cout << "copy2" << std::endl;
}
};
int main(){
Foo f;
Foo f2 (f);
}
it prints "template". I expected it to print "copy2" because it is typed parameters. Why is it using template instead?
In order to perform overload resolution, the compiler needs to evaluate the constructor template using template argument deduction. So it creates a constructor like this:
template<>
Foo<Foo>::Foo(Foo&);
This constructor takes an lvalue reference to non-const, as oppose to the non-template constructor. This is preferred because an identity conversion (i.e no conversion) is preferred over a qualification conversion.
Foo f;
f is not a const object of Foo. So its type match to following function and it will print template.
template<typename T>
Foo (T&) {
std::cout << "template" << std::endl;
}
If you define f as follows, it will print as you expected.
const Foo f;
For the same reason that your first code sample doesn't print copy2. The f you are referencing is not const, so the compiler first tries non-const references. Overload matching matches template<typename T=Foo> Foo(T&) giving an exact match while Foo(const Foo&) requires a cv-adjustment of the parameter being passed.
Try the following:
#include <iostream>
class Foo {
public:
Foo(){};
template<typename T>
Foo (T&) {
std::cout << "template" << std::endl;
}
//Foo(Foo&) {
// std::cout << "copy" << std::endl;
//}
Foo(const Foo&) {
std::cout << "copy2" << std::endl;
}
};
int main(){
const Foo f;
Foo f2 (f);
}
http://ideone.com/zOf1qX