I have a templated class A<T, int> and two typedefs A<string, 20> and A<string, 30>.
How do I override the constructor for A<string, 20> ? The following does not work:
template <typename T, int M> class A;
typedef A<std::string, 20> one_type;
typedef A<std::string, 30> second_type;
template <typename T, int M>
class A {
public:
A(int m) {test= (m>M);}
bool test;
};
template<>
one_type::one_type() { cerr << "One type" << endl;}
I would like the class A<std::string,20> to do something that the other class doesn't. How can I do this without changing the constructor A:A(int) ?
The only thing you cannot do is use the typedef to define the constructor. Other than that, you ought to specialize the A<string,20> constructor like this:
template<> A<string,20>::A(int){}
If you want A<string,20> to have a different constructor than the generic A, you need to specialize the whole A<string,20> class:
template<> class A<string,20> {
public:
A(const string& takethistwentytimes) { cerr << "One Type" << std::endl; }
};
Assuming your really meant for A::test to be publicly accessible, you could do something like this:
#include <iostream>
template <int M>
struct ABase
{
ABase(int n) : test_( n > M )
{}
bool const test_;
};
template <typename T, int M>
struct A : ABase<M>
{
A(int n) : ABase<M>(n)
{}
};
template <typename T>
A<T, 20>::A(int n)
: ABase<20>(n)
{ std::cerr << "One type" << std::endl; }
Kick the tires:
int main(int argc, char* argv[])
{
A<int, 20> a(19);
std::cout << "a:" << a.test_ << std::endl;
A<int, 30> b(31);
std::cout << "b:" << b.test_ << std::endl;
return 0;
}
Late but a very elegant solution:
C++ 2020 introduced Constraints and Concepts. You can now conditionally enable and disable constructors and destructors!
#include <iostream>
#include <type_traits>
template<class T>
struct constructor_specialized
{
constructor_specialized() requires(std::is_same_v<T, int>)
{
std::cout << "Specialized Constructor\n";
};
constructor_specialized()
{
std::cout << "Generic Constructor\n";
};
};
int main()
{
constructor_specialized<int> int_constructor;
constructor_specialized<float> float_constructor;
};
Run the code here.
This may be a little bit late, but if you have access to c++11 you can use SFINAE to accomplish just what you want:
template <class = typename std::enable_if<
std::is_same<A<T,M>, A<std::string, 20>>::value>::type // Can be called only on A<std::string, 20>
>
A() {
// Default constructor
}
Working example
How about :
template<typename T, int M, bool dummy = (M > 20) >
class A {
public:
A(int m){
// this is true
}
};
template<typename T, int M>
class A<T,M,false> {
public:
A(int m) {
//something else
}
};
You can't with your current approach. one_type is an alias to a particular template specialization, so it gets whatever code the template has.
If you want to add code specific to one_type, you have to declare it as a subclass of A specialization, like this:
class one_type:
public A<std::string, 20>
{
one_type(int m)
: A<str::string, 20>(m)
{
cerr << "One type" << endl;
}
};
The best solution I've been able to come up with for this situation is to use a "constructor helper function":
template <typename T, int M> class A;
typedef A<std::string, 20> one_type;
typedef A<std::string, 30> second_type;
template <typename T, int M>
class A {
private:
void cons_helper(int m) {test= (m>M);}
public:
A(int m) { cons_helper(m); }
bool test;
};
template <>
void one_type::cons_helper(int) { cerr << "One type" << endl;}
Related
Please, before marking this as a duplicate of This question read the entirety of the post
This piece of code fails to compile, with a template deduction error:
#include <iostream>
#include <type_traits>
template<typename T = float, int N>
class MyClass
{
public:
template<typename DATA_TYPE>
using MyType = std::conditional_t<(N>0), DATA_TYPE, double>;
MyType<T> Var;
void Foo()
{
Bar(Var);
}
template<typename TYPE>
void Bar(MyType<TYPE> Input)
{
std::cout << typeid(Input).name() << std::endl;
}
};
int main()
{
MyClass<float, 1> c;
c.Foo();
return 0;
}
I understand the point that was made in the question i linked above, which is that "the condition which allows to choose the type to be deduced depends on the type itself", however, why would the compiler fail in the specific case i provided as the condition here seems to be fully independent from the type, or is there something i'm missing?
I would be more than happy if someone could refer to a section of the c++ standard that would allow me to fully understand this behaviour.
As the linked question, TYPE is non deducible. MyType<TYPE> is actually XXX<TYPE>::type.
You have several alternatives, from your code, I would say one of
Bar no longer template:
template<typename T = float, int N>
class MyClass
{
public:
template<typename DATA_TYPE>
using MyType = std::conditional_t<(N>0), DATA_TYPE, double>;
MyType<T> Var;
void Foo()
{
Bar(Var);
}
void Bar(MyType<T> Input)
{
std::cout << typeid(Input).name() << std::endl;
}
};
requires (or SFINAE/specialization for pre-c++20):
template<typename T = float, int N>
class MyClass
{
public:
template<typename DATA_TYPE>
using MyType = std::conditional_t<(N>0), DATA_TYPE, double>;
MyType<T> Var;
void Foo()
{
Bar(Var);
}
template<typename TYPE>
void Bar(TYPE Input) requires(N > 0)
{
std::cout << typeid(Input).name() << std::endl;
}
void Bar(double Input) requires(N <= 0)
{
std::cout << typeid(Input).name() << std::endl;
}
};
I have a template class and a member function print() to print the data.
template<typename T>
class A
{
public:
T data;
void print(void)
{
std::cout << data << std::endl;
}
// other functions ...
};
Then, I want to either print scalar data or vector data, so I give a specialized definition and get a compiler error.
template<typename T>
void A<std::vector<T>>::print(void) // template argument list error
{
for (const auto& d : data)
{
std::cout << d << std::endl;
}
}
Question: Why does this member function specialization get an error? What is the correct way to define a print function for a vector?
Solution 1: I have tested the following definition.
template<typename T>
class A<std::vector<T>>
{
public:
std::vector<T> data;
void print(void) { // OK
// ...
}
}
This one worked, but I have to copy the other member functions into this specialized class.
EDIT:
Solution 2: To prevent copy all the other member functions, I define a base class containing the common member functions and inherit from the base class:
template<typename T>
class Base
{
public:
T data;
// other functions ...
};
template<typename T>
class A : public Base<T>
{
public:
void print(void)
{
std::cout << this->data << std::endl;
}
};
template<typename T>
class A<std::vector<T>> : public Base<std::vector<T>>
{
public:
void print(void)
{
for (const auto& d : this->data)
{
std::cout << d << std::endl;
}
}
};
This solution works well. Are there some better or more conventional solutions?
Why does this member function specialization get error?
When you instantiate the template class A for example A<std::vector<int>>, the template parameter T is equal to std::vector<int>, not std::vector<T>, and this a specialization case of the function. Unfortunately this can not be done with member functions as mentioned in the comments.
Are there some better solutions?
Yes; In c++17 you could use if constexpr with a trait to check the std::vector, like this.
#include <type_traits> // std::false_type, std::true_type
#include <vector>
// traits for checking wether T is a type of std::vector<>
template<typename T> struct is_std_vector final : std::false_type {};
template<typename... T> struct is_std_vector<std::vector<T...>> final : std::true_type {};
template<typename T>
class A /* final */
{
T mData;
public:
// ...constructor
void print() const /* noexcept */
{
if constexpr (is_std_vector<T>::value) // when T == `std::vector<>`
{
for (const auto element : mData)
std::cout << element << "\n";
}
else // for types other than `std::vector<>`
{
std::cout << mData << std::endl;
}
}
};
(See Live Online)
This way you keep only one template class and the print() will instantiate the appropriate part according to the template type T at compile time.
If you don not have access to C++17, other option is to SFINAE the members(Since c++11).
#include <type_traits> // std::false_type, std::true_type, std::enbale_if
#include <vector>
// traits for checking wether T is a type of std::vector<>
template<typename T> struct is_std_vector final : std::false_type {};
template<typename... T> struct is_std_vector<std::vector<T...>> final : std::true_type {};
template<typename T>
class A /* final */
{
T mData;
public:
// ...constructor
template<typename Type = T> // when T == `std::vector<>`
auto print() const -> typename std::enable_if<is_std_vector<Type>::value>::type
{
for (const auto element : mData)
std::cout << element << "\n";
}
template<typename Type = T> // for types other than `std::vector<>`
auto print() const -> typename std::enable_if<!is_std_vector<Type>::value>::type
{
std::cout << mData << std::endl;
}
};
(See Live Online)
What if I have more other data types like self-define vector classes
or matrices? Do I have to define many is_xx_vector?
You can check the type is a specialization of the provided one like as follows. This way you can avoid providing many traits for each type. The is_specialization is basically inspired from this post
#include <type_traits> // std::false_type, std::true_type
#include <vector>
// custom MyVector (An example)
template<typename T> struct MyVector {};
template<typename Test, template<typename...> class ClassType>
struct is_specialization final : std::false_type {};
template<template<typename...> class ClassType, typename... Args>
struct is_specialization<ClassType<Args...>, ClassType> final : std::true_type {};
And the print function could be in c++17:
void print() const /* noexcept */
{
if constexpr (is_specialization<T, std::vector>::value)// when T == `std::vector<>`
{
for (const auto element : mData)
std::cout << element << "\n";
}
else if constexpr (is_specialization<T, ::MyVector>::value) // custom `MyVector`
{
std::cout << "MyVector\n";
}
else // for types other than `std::vector<>` and custom `MyVector`
{
std::cout << mData << std::endl;
}
}
(See Live Online)
You need to implement a template class that uses a vector as template parameter. This worked for me.
template<typename T>
class A
{
public:
T data;
void print(void) {
std::cout << "Data output" << std::endl;
}
// other functions ...
};
template <typename T>
class A<std::vector<T>>
{
public:
std::vector<T> data;
void print() {
for (auto i : data) {
std::cout << "Vector output" << std::endl;
}
}
};
You could always use named tag dispatching to check if type provided by template user is vector.
A<std::vector<T>> notation won't work as you both try to take into account that T is type and vector of types which is contradicting with itself.
Below is code I used named tag dispatching as solution to your problem:
#include <iostream>
#include <vector>
#include <type_traits>
using namespace std;
template<typename T> struct is_vector : public std::false_type {};
template<typename T, typename A>
struct is_vector<std::vector<T, A>> : public std::true_type {};
template<typename T>
class A
{
public:
T data;
void print(std::true_type) {
for (auto& a : data) { std::cout << a << std::endl; }
}
void print(std::false_type) {
std::cout << data << std::endl;
}
void print() {
print(is_vector<T>{});
}
};
int main()
{
A<int> a;
a.data = 1;
a.print();
A<std::vector<int>> b;
b.data = { 1, 2 ,3 ,4 ,5 };
b.print();
return 0;
}
Succesfully compiled with https://www.onlinegdb.com/online_c++_compiler
Based on answer: Check at compile-time is a template type a vector
You can dispatch printing to another member function (static or not). For example:
template<typename T>
class A {
public:
T data;
void print() const {
print_impl(data);
}
private:
template<class S>
static void print_impl(const S& data) {
std::cout << data;
}
template<class S, class A>
static void print_impl(const std::vector<S, A>& data) {
for (const auto& d : data)
std::cout << d;
}
};
I would like to do something like this:
#include <iostream>
class a {
public:
a() : i(2) {}
template <typename ...ts>
void exec() {
f<ts...>();
std::cout << "a::()" << std::endl;
}
int i;
private:
template <typename t>
void f() {
i += t::i;
}
template <typename t, typename ...ts>
void f() {
f<t>();
f<t, ts...>();
}
};
struct b {
static const int i = -9;
};
struct c {
static const int i = 4;
};
int main()
{
a _a;
_a.exec<b,c>();
std::cout << _a.i << std::endl;
}
The idea is to get the same information from a group of classes, without the need of an object of each class.
Does anyone know if it is possible?
Thanks!
In case Your compiler does not support C++17:
template <typename ...ts>
void f() {
for ( const auto &j : { ts::i... } )
i += j;
}
In C++17, your class would simply be
class a {
public:
a() : i(2) {}
template <typename ...ts>
void exec() {
((i += ts::i), ...); // Folding expression // C++17
std::cout << "a::()" << std::endl;
}
int i;
};
Possible in C++11 too, but more verbose.
Reasons why your code is not compiling:
Syntax of specializing templates is a little different.
You need to put the most general case first.
You can't partially specialize functions, only classes.
Partial specialization is not allowed within classes, only in namespaces.
Here is an example for C++11.
#include <iostream>
template<typename t, typename ...ts>
class a {
public:
static constexpr int x = t::i + a<ts...>::x;
};
template<typename t>
class a<t> {
public:
static constexpr int x = 2 + t::i;
};
struct b {
static constexpr int i = -9;
};
struct c {
static constexpr int i = 4;
};
int main()
{
constexpr int result = a<b,c>::x;
std::cout << result << std::endl;
}
Remember that templates are calculated during compilation so, for optimization sake, it is a good idea to write them in a way that allows them to be constexpr.
I want to specialise a single template method in a non-template class to use an std::vector however only the return type of the method uses the template.
#include <iostream>
#include <string>
#include <vector>
class Foo
{
public:
template<typename T>
T Get()
{
std::cout << "generic" << std::endl;
return T();
}
};
template<>
int Foo::Get()
{
std::cout << "int" << std::endl;
return 12;
}
template<typename T>
std::vector<T> Foo::Get()
{
std::cout << "vector" << std::endl;
return std::vector<T>();
}
int main()
{
Foo foo;
auto s = foo.Get<std::string>();
auto i = foo.Get<int>();
}
This compiles with an error indicating that the std::vector attempted specialisation does not match any prototype of Foo, which is completely understandable.
In case it matters, use of C++14 is fine and dandy.
You can only partially specialize classes (structs) (cppreference) - so the way to overcome your problems is to add helper struct to allow this partial specialization of std::vector<T> - e.g. this way:
class Foo
{
private: // might be also protected or public, depending on your design
template<typename T>
struct GetImpl
{
T operator()()
{
std::cout << "generic" << std::endl;
return T();
}
};
public:
template<typename T>
auto Get()
{
return GetImpl<T>{}();
}
};
For int - you can fully specialize this function:
template<>
int Foo::GetImpl<int>::operator()()
{
std::cout << "int" << std::endl;
return 12;
}
For std::vector<T> you have to specialize entire struct:
template<typename T>
struct Foo::GetImpl<std::vector<T>>
{
std::vector<T> operator()()
{
std::cout << "vector" << std::endl;
return std::vector<T>();
}
};
Partial specialisation of template functions (including member functions) is not allowed. One option is to overload instead using SFINAE. For example,
/// auxiliary for is_std_vetor<> below
struct convertible_from_std::vector
{
template<typename T>
convertible_from_std::vector(std::vector<T> const&);
};
template<typename V>
using is_std_vector
= std::is_convertible<V,convertible_from_std_vector>;
class Foo
{
public:
template<typename T, std::enable_if_t< is_std::vector<T>::value,T>
Get()
{
std::cout << "vector" << std::endl;
return T();
}
template<typename T, std::enable_if_t<!is_std::vector<T>::value,T>
Get()
{
std::cout << "generic" << std::endl;
return T();
}
};
Note that the helper class is_std_vector may be useful in other contexts as well, so it worth having somewhere. Note further that you can make this helper class more versatile by asking for any std::vector or specific std::vector<specific_type, specific_allocator>. For example,
namespace traits {
struct Anytype {};
namespace details {
/// a class that is convertible form C<T,T>
/// if either T==AnyType, any type is possible
template<template<typename,typename> C, typename T1=Anytype,
typename T2=Anytype>
struct convCtTT
{
convCtTT(C<T1,T2> const&);
};
template<template<typename,typename> C, typename T1=Anytype>
struct convCtTT<C,T1,AnyType>
{
template<typename T2>
convCtTT(C<T1,T2> const&);
};
template<template<typename,typename> C, typename T2=Anytype>
struct convCtTT<C,AnyType,T2>
{
template<typename T1>
convCtTT(C<T1,T2> const&);
};
template<template<typename,typename> C>
struct convCtTT<C,AnyType,AnyType>
{
template<typename T1, typename T2>
convCtTT(C<T1,T2> const&);
};
}
template<typename Vector, typename ValueType=AnyType,
typename Allocator=AnyType>
using is_std_vector
= std::is_convertible<Vector,details::convCtTT<std::vector,ValueType,
Allocator>;
}
You can't partially specialze template in c++. You need to overload your function and pass the type in parameters.
#include <iostream>
#include <string>
#include <vector>
class Foo
{
public:
template<typename T>
T Get()
{
return this->getTemplate(static_cast<T*>(0)); //
}
private:
template<class T> T getTemplate(T* t)
{
std::cout << "generic" << std::endl;
return T();
}
template<class T> std::vector<T> getTemplate(std::vector<T>* t)
{
std::cout << "vector" << std::endl;
return std::vector<T>();
}
};
template <> int Foo::getTemplate(int* t)
{
std::cout << "int" << std::endl;
return 12;
}
int main()
{
Foo foo;
auto s = foo.Get<std::string>();
auto i = foo.Get<int>();
auto v = foo.Get<std::vector<int>>();
}
Edit : fixed a typo in the code
I don't know if this is possible, but I would like to understand better how this works.
Can a class implict convertsion operation be used to match a template parameter?
This is what I want to do.
#include <iostream>
template<typename T>
struct Value {
};
template<>
struct Value<int> {
static void printValue(int v) {
std::cout << v << std::endl;
}
};
struct Class1 {
int value;
};
/*
template<>
struct Value<Class1*> {
static void printValue(Class1* v) {
std::cout << v->value << std::endl;
}
};
*/
template<typename X>
struct ClassContainer {
ClassContainer(X *c) : _c(c) {}
operator X*() { return _c; }
X *_c;
};
template<typename X>
struct Value<ClassContainer<X>> {
static void printValue(ClassContainer<X> v) {
std::cout << static_cast<X*>(v)->value << std::endl;
}
};
template<typename X>
void doPrintValue(X v)
{
Value<X>::printValue(v);
}
int main(int argc, char *argv[])
{
doPrintValue(10);
Class1 *c = new Class1{ 20 };
//doPrintValue(c); // error C2039: 'printValue': is not a member of 'Value<X>'
ClassContainer<Class1> cc(c);
doPrintValue(cc);
std::cout << "PRESS ANY KEY TO CONTINUE";
std::cin.ignore();
}
ClassContainer has an implict conversion to X*. Is it possible to match ClassContainer passing only X*?
If you want the template class for pointers to behave like the template class for something else, just inherit:
template<typename T>
struct Value<T*> : Value<ClassContainer<T>> {};
It will inherit the public printValue function, which accepts a parameter that can be constructed from T*, and everything will be implicitly converted as expected.
See it all live here.