Can you call different constructors based on different template parameters - c++

Is there an way to call different constructors of same class based on different template parameter.
template<class T>
class X {
public:
T a;
X<char>() {
std::cout << "char ctor called" << std::endl;
}
X<int>() {
std::cout << "int ctor called" << std::endl;
}
};
int main() {
X<char> x;
X<int> y;
}
I think constructors in the class are not valid, but is there any other way to do that?

There are several ways:
(full) specialization of the member:
template<class T>
class X {
public:
T a;
X();
};
template <>
X<char>::X() : a('\0') { std::cout << "char ctor called" << std::endl; }
template <>
X<int>::X() : a(0) { std::cout << "int ctor called" << std::endl; }
Specialize the whole class (so requires to duplicate some code though)
template<class T>
class X;
template <>
class X<char> {
public:
char a;
X() : a('\0') { std::cout << "char ctor called" << std::endl; }
};
template <>
class X<int> {
public:
int a;
X() : a(0) { std::cout << "int ctor called" << std::endl; }
};
if constexpr of C++17 (but doesn't handle initializer list):
template<class T>
class X {
public:
T a;
X() : a(0) {
if constexpr (std::is_same_v<char, T>) {
std::cout << "char ctor called" << std::endl;
} else if constexpr (std::is_same_v<int, T>) {
std::cout << "int ctor called" << std::endl;
}
}
};
requires for C++20:
template<class T>
class X {
public:
T a;
X() requires(std::is_same_v<char, T>) : a('\0') {
std::cout << "char ctor called" << std::endl;
}
X() requires(std::is_same_v<int, T>) : a(0) {
std::cout << "int ctor called" << std::endl;
}
};

You can use if constexpr:
template <class T>
class X {
public:
// ...
X()
{
if constexpr (std::is_same_v<T, int>) {
// ...
} else if constexpr (std::is_same_v<T, char>) {
// ...
} else {
// ...
}
}
};

Just another alternative - tag dispatch:
template<class T>
class X {
public:
// ...
X() : X(Tag<T>{}) {}
private:
template<class> struct Tag {};
X(Tag<char>) {
// ...
}
X(Tag<int>) {
// ...
}
template<class U> X(Tag<U>) {
// ...
}
};

Yes,see http://www.cplusplus.com/doc/oldtutorial/templates/
You can do this:
template<class T>
class X {
public:
T a;
X() {
std::cout << "basic template\n" ;
}
};
template<>
class X<char> {
public:
char a;
X() {
std::cout << "char ctor called\n" ;
}
};
template<>
class X<int> {
public:
int a;
X() {
std::cout << "int ctor called\n";
}
};
int main(){
X<int> xint;
X<char> xchar;
X<string> xstring;
return 0;
}
You will see in the output window:
int ctor called
char ctor called
basic template

Related

How to direct initialize a C++ variant with one of its non-default option?

Consider the following:
class Foo {
[...]
public:
Foo(int);
}
class Bar {
[...]
public:
Bar(int);
}
using MyVariant = std::variant<Foo,Bar>;
How can one direct initialize (i.e no copy and no Foo building involved) an instance of MyVariant as a Bar ?
Pass the a tag of type std::in_place_type_t<Bar> as first constructor parameter:
struct Foo
{
Foo(int value)
{
std::cout << "Foo(" << value << ")\n";
}
};
struct Bar
{
Bar(int value)
{
std::cout << "Bar(" << value << ")\n";
}
Bar(Bar const&)
{
std::cout << "Bar(Bar const&)\n";
}
Bar& operator=(Bar const&)
{
std::cout << "Bar& operator=(Bar const&)\n";
}
};
int main() {
std::variant<Foo, Bar> var(std::in_place_type_t<Bar>{}, 1);
}
std::in_place_index_t would be an alternative first parameter, if you prefer passing the position of the type in the template parameter list of std::variant.

std::make_shared and protected/private constructors

[Edit: Turns out we don't even need reinterpret cast, making this even simpler]
This came up here and I found a better solution using reinterpret cast and shared pointer aliasing constructor. It allows both the ctor and dtor to be private, as well as use of the final specifier.
The reputation system won't let me leave this as an answer in that question, so I had to provide it as another question...
#include <iostream>
#include <memory>
class Factory final {
public:
template<typename T, typename... A>
static std::shared_ptr<T> make_shared(A&&... args) {
auto ptr = std::make_shared<Type<T>>(std::forward<A>(args)...);
return std::shared_ptr<T>(ptr, &ptr->type);
}
private:
template<typename T>
struct Type final {
template<typename... A>
Type(A&&... args) : type(std::forward<A>(args)...) { std::cout << "Type(...) addr=" << this << "\n"; }
~Type() { std::cout << "~Type()\n"; }
T type;
};
};
class X final {
friend struct Factory::Type<X>; // factory access
private:
X() { std::cout << "X() addr=" << this << "\n"; }
X(int i) { std::cout << "X(...) addr=" << this << " i=" << i << "\n"; }
~X() { std::cout << "~X()\n"; }
};
int main() {
auto ptr1 = Factory::make_shared<X>();
auto ptr2 = Factory::make_shared<X>(42);
}
Giving the following output under gcc...
X() addr=0x62bc30
Type(...) addr=0x62bc30
X(...) addr=0x62bc50 i=42
Type(...) addr=0x62bc50
~Type()
~X()
~Type()
~X()
Just a followup... The approach above doesn't play well with std::enable_shared_from_this<> because the initial std::shared_ptr<> is to the wrapper and not the type itself. We can address this with an equivalent class that is compatible with the factory...
#include <iostream>
#include <memory>
template<typename T>
class EnableShared {
friend class Factory; // member access
public:
std::shared_ptr<T> shared_from_this() { return weak.lock(); }
protected:
EnableShared() = default;
virtual ~EnableShared() = default;
EnableShared<T>& operator=(const EnableShared<T>&) { return *this; } // no slicing
private:
std::weak_ptr<T> weak;
};
class Factory final {
public:
template<typename T, typename... A>
static std::shared_ptr<T> make_shared(A&&... args) {
auto ptr = std::make_shared<Type<T>>(std::forward<A>(args)...);
auto alt = std::shared_ptr<T>(ptr, &ptr->type);
assign(std::is_base_of<EnableShared<T>, T>(), alt);
return alt;
}
private:
template<typename T>
struct Type final {
template<typename... A>
Type(A&&... args) : type(std::forward<A>(args)...) { std::cout << "Type(...) addr=" << this << "\n"; }
~Type() { std::cout << "~Type()\n"; }
T type;
};
template<typename T>
static void assign(std::true_type, const std::shared_ptr<T>& ptr) {
ptr->weak = ptr;
}
template<typename T>
static void assign(std::false_type, const std::shared_ptr<T>&) {}
};
class X final : public EnableShared<X> {
friend struct Factory::Type<X>; // factory access
private:
X() { std::cout << "X() addr=" << this << "\n"; }
X(int i) { std::cout << "X(...) addr=" << this << " i=" << i << "\n"; }
~X() { std::cout << "~X()\n"; }
};
int main() {
auto ptr1 = Factory::make_shared<X>();
auto ptr2 = ptr1->shared_from_this();
std::cout << "ptr1=" << ptr1.get() << "\nptr2=" << ptr2.get() << "\n";
}

Why can't I call operator()?

I am trying to understand different topics in C++ by examples and I cannot get this example to work:
template<typename T>
class zero_init
{
T val;
public:
zero_init() : val(static_cast<T>(0)) { std::cout << "In constructor with no parameters\n"; }
operator T() const { std::cout << "In operator T()\n"; return val; }
};
int main()
{
const zero_init<int> x;
x(); //Error!
return 0;
}
I am obviously trying to call the operator() but it gives the error: "call of an object of a class type without appropriate operator()"
You accidentally implemented a type conversion operator and not operator(). Overload operator() like this instead (I removed the return value because you discard it in main anyway):
#include <iostream>
template<typename T>
class zero_init
{
T val;
public:
zero_init() : val(static_cast<T>(0)) { std::cout << "In constructor with no parameters\n"; }
void operator()() const { std::cout << "In operator()\n"; }
};
int main()
{
const zero_init<int> x;
x();
return 0;
}
If you actually need the return value, do it like this:
#include <iostream>
template<typename T>
class zero_init
{
T val;
public:
zero_init() : val(static_cast<T>(0)) { std::cout << "In constructor with no parameters\n"; }
T operator()() const { std::cout << "In operator()\n"; return val; }
};
int main()
{
const zero_init<int> x;
auto val = x();
return 0;
}

Overloading function templates and inherited arguments

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

Default template arguments compiler bug

Next code:
#include <typeinfo>
#include <iostream>
struct A
{
A() : _m('a'){ std::cout << "A()" << std::endl; }
void f(){ std::cout << "A::f() " << _m << std::endl; }
char _m;
};
struct B
{
B() : _m('b'){ std::cout << "B()" << std::endl; }
void f(){ std::cout << "B::f() " << _m << std::endl; }
char _m;
};
struct C
{
C() : _m('c'){ std::cout << "C()" << std::endl; }
void f(){ std::cout << "C::f() " << _m << std::endl; }
char _m;
};
template<typename T>
void f(T t = T());
template<typename T>
void f(T t)
{
std::cout << typeid(t).name() << std::endl;
t.f();
}
int main()
{
f<A>();
f<B>();
f<C>();
}
Has this output when using VS2008, VS2010 and VS2012:
A()
struct A
A::f() a
A()
struct B
B::f() a
A()
struct C
C::f() a
Is this a known compiler bug?
Please note that it works as expected in VS2013.
Your compiler might be confused because you have a function template declaration, followed by something that looks like a function template partial specialization. GCC correctly rejects your code.
To be precise, this is the problem:
template<typename T>
void f<T>(T t) { .... }
// ^^^
If you really want to separate declaration and definition, you would need
template<typename T>
void f(T t) { .... }
This would be a well-formed version of your program:
#include <iostream>
#include <typeinfo>
struct A {}; // as before
struct B {}; // as before
struct C {}; // as before
template<typename T>
void f(T t = T())
{
std::cout << typeid( t ).name() << std::endl;
t.f();
}
int main()
{
f<A>();
f<B>();
f<C>();
}