Template function deduction fail on std::conditional argument - c++

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;
}
};

Related

Enable Different Member Functions Depending On If Type Is POD

I believe this is fairly simply to achieve, but I can't figure out how.
Take the following example class:
class Example {
public:
template <typename T>
size_t foo(T& v) const;
};
How can I provide two implementations for this method depending on if T is a POD? I know there is an std::is_pod type trait, but I can't figure out how to have it enable the correct function.
I also need to be able to provide specific specializations for certain types of T regardless of if they are PODs or not. For example, I want to be able to write:
template <>
size_t foo<uint8_t>(uint8_t& b);
While all other types of T are chosen based upon being PODs or not.
EDIT
I have been looking at the information everyone has been giving and have come up with the following, however, this still does not work (throws a compiler error). I can't understand why.
class Example {
public:
template <typename T, bool U = std::is_trivially_copyable<T>::value>
size_t foo(T& v) const;
};
template <typename T>
size_t Example::foo<T, true>(T& v) const {
//Do something if T is mem copyable
}
template <typename T>
size_t Example::foo<T, false>(T& v) const {
//Do something if T is not mem copyable
}
Which results in "non-class, non-variable partial specialization 'foo<T, true>' is not allowed"
You can do it in the following way.
#include <iostream>
#include <type_traits>
struct A {
int m;
};
struct B {
int m1;
private:
int m2;
};
struct C {
virtual void foo() {};
};
// is_pod is deprecated since C++ 20
template <typename T>
std::enable_if_t<std::is_pod_v<T>, size_t> foo(const T& pod)
{
std::cout << &pod << " is_pod\n";
return 0;
}
template <>
size_t foo<uint8_t>(const uint8_t& b)
{
std::cout << int(b) << " is uint8_t\n";
return 1;
}
template <>
size_t foo<int>(const int& b)
{
std::cout << int(b) << " is int\n";
return 1;
}
int main()
{
uint8_t a = 2;
foo(a);
foo(22);
// will compile
A instance;
foo(instance);
// will not compile
B b;
//foo(b);
// will not compile
C c;
//foo(c);
}
output
2 is uint8_t
22 is int
010FFA64 is_pod
Here is what I did to get it to work correctly:
#include <type_traits>
#include <iostream>
class Example {
public:
Example(int a) : _a(a) {}
virtual void bar() {}
template <typename T, typename std::enable_if<std::is_trivially_copyable<T>::value, size_t>::type = 0>
size_t foo(T& v) const {
std::cout << "T is trivially copyable" << std::endl;
return sizeof(T);
}
template <typename T, typename std::enable_if<!std::is_trivially_copyable<T>::value, size_t>::type = 0>
size_t foo(T& v) const {
std::cout << "T is not trivially copyable" << std::endl;
return sizeof(T);
}
private:
int _a;
int _b;
int _c;
};
template <>
size_t Example::foo<unsigned int>(unsigned int& v) const {
std::cout << "T is unsigned int" << std::endl;
return sizeof(unsigned int);
}
int main() {
Example e(10);
int a;
e.foo(a);
char b;
e.foo(b);
e.foo(e);
unsigned int c;
e.foo(c);
return 0;
}
The output of which looks like:
T is trivially copyable
T is trivially copyable
T is not trivially copyable
T is unsigned int
This is based upon the following Provide/enable method to in a class based on the template type

variadic template only using type parameter

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.

How to specialise template method with type that itself is a template where only the return type relies on the template type?

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

template friend function: wrong function called

I'm trying to overload a function inside template struct using friend.
I want to use that to map a type to another type. Here in the code below I want to map the type int to MyType.
Here's what I did so far:
void map(...){} // Worst case
// Here's the class that will overload our function
template<typename Type, typename T>
struct MakeFunction {
friend Type map(T) { return {}; }
};
// Make the function with int?
struct MyType : MakeFunction<MyType, int> {};
int main() {
// The type obtained is void, worst case choosed. The expected result is `MyType` as return type.
std::cout << typeid(decltype(map(int{}))).name() << std::endl;
return 0;
}
Then, I tried that:
template<typename T>
void map(){} // Worst case
// Here's the class that will overload our function
template<typename Type, typename T>
struct MakeFunction {
// Compilation error.
friend Type map<T>() { return {}; }
};
struct MyType : MakeFunction<MyType, int> {};
int main() {
std::cout << typeid(decltype(map<int>())).name() << std::endl;
return 0;
}
But the compilation failed with :
error: defining explicit specialization ’map<T>’ in friend delcaration
How can I change the declaration so the right function is picked? Or is there a way to map types without a ton a boilerplate?
Below code shows how you can define a macro DEFINE_TYPE_MAPPING meeting your needs (this is to some extent a sketch demonstrating the idea):
#include <iostream>
#include <typeinfo>
void map(...){} // Worst case
template<class T> struct TypeMapping;
template<class T>
typename TypeMapping<T>::type map(const T&);
#define DEFINE_TYPE_MAPPING(T, U) \
template<> struct TypeMapping<T> { typedef U type; };
struct MyType {};
DEFINE_TYPE_MAPPING(int, MyType);
DEFINE_TYPE_MAPPING(char, float*);
DEFINE_TYPE_MAPPING(std::ostream, unsigned long);
int main() {
std::cout << typeid(decltype(map(int{}))).name() << std::endl;
std::cout << typeid(decltype(map('c'))).name() << std::endl;
std::cout << typeid(decltype(map(std::cout))).name() << std::endl;
std::cout << typeid(decltype(map(1.0))).name() << std::endl;
return 0;
}
How about:
namespace detail{
// To keep exact type
template <typename> struct tag {};
// The mapping
float map(tag<char>);
MyType map(tag<int>);
char map(tag<const int&>);
// ... and so on
}
template <typename T>
using map_t = decltype(detail::map(detail::tag<T>{}));
And then
int main() {
std::cout << typeid(map_t<int>).name() << std::endl;
std::cout << typeid(map_t<const int&>).name() << std::endl;
}

C++ How do I cast from 1 template class to another?

I have a class which I intend to use with either the type float or double. As far as I am aware, there is no way to restrict template options, so perhaps I might be doing something dangerous here?
template<class T>
class A
{
A(T arg) { _data = arg; }
T _data;
}
typedef A<float> A_f;
typedef A<double> A_d;
How can I do the following?
int main()
{
A_f f(3.1415);
A_d d(3.1415);
f = (A_f)d;
}
IE: Cast the class containing data of type double to the class containing data of type float.
Edit: This doesn't seem to be going anywhere, so I tried playing around with this, but obviously I have no idea what to do here so it doesn't compile...
template<class T>
class A
{
friend // Intention is for T to be double here
A<float> operator A<float>(const A<T> input);
}
A<float> operator A<float>(const A<double> input)
{
return A<float>(input._data);
}
Maybe this helps explain what I want to achieve?
Second Edit for Adam:
return A<float>((float)input._data);
Is this better?
You could use std::enable_if to only allow certain types :
#include <type_traits>
using namespace std;
// Our catch-all is not defined, so will not compile
// Could also be made to print a nice error message
template<typename T, typename Sfinae = void> class A;
// Ok if T is float or double
template<typename T>
class A<T, typename std::enable_if<std::is_same<T, float>::value
|| std::is_same<T, double>::value>::type>
{
// Your class here
};
int main()
{
A<int> a; // FAILS
A<float> b; // Ok
A<double> c; // Ok
return 0;
}
Then you just need to define a conversion operator in your class for the cast to work.
Do not cast, but provide one (and only one) implicit conversion constructor or conversion operator. In your case it might be as trivial as operator T () const { return _data; }
I'll second the arguments that you should not cast like that, but if you insist, add a templated copy constructor:
template<class T>
class A
{
public: // add this
A(T arg) { _data = arg; }
template <class U> // add this
A(A<U> arg) { _data = arg._data; } // add this
T _data;
}
This will then allow conversions from A<U> to A<T> as long as U is implicitly convertible to T.
There is an option to customize a class based on the type you provide it. The technique is called 'template specialization'
#include <iostream>
using namespace std;
template <typename T>
class A {
public:
void print_my_type() {
cout << "Generic template instance" << endl;
}
explicit operator A<int>() const {
cout << "Casting to int" << endl;
return A<int>();
}
};
template <>
class A<int> {
public:
void print_my_type() {
cout << "Class templated with an int" << endl;
}
explicit operator A<double>() const {
cout << "Casting to double" << endl;
return A<double>();
}
};
int main() {
A<double> a;
A<int> b;
a.print_my_type();
b.print_my_type();
a = static_cast<A<double>>(b);
return 0;
}
You should not cast objects like that. If you intend to have an object that you want to cast to another. You should provide it an operator A() method so it can handle conversions gracefully
Example with template conversion operator + static_assert for type verify:
http://coliru.stacked-crooked.com/a/6b01010ea5f02aee
#include <vector>
#include <iostream>
template < typename T > class TD; // type visualiser
template<class T>
class A
{
public:
A(T arg) { _data = arg; }
template<typename D>
operator A<D>() {
static_assert(std::is_same<D, float>::value || std::is_same<D, double>::value, "double/floats allowed only");
//TD<D>(); // D is float here
return static_cast<D>(_data);
}
T _data;
};
typedef A<float> A_f;
typedef A<double> A_d;
typedef A<int> A_i;
int main() {
A_f f(3.14151);
A_d d(3.14152);
std::cout << f._data << std::endl;
std::cout << d._data << std::endl;
f = (A_f)d;
//f = (A_i)d; // static assertion here
std::cout << f._data << std::endl;
return 0;
}
[edit]
template<class T>
class A
{
public:
A(T arg) { _data = arg; }
template<typename D>
operator A<D>() {
static_assert(std::is_same<D, float>::value || std::is_same<D, double>::value, "double/floats allowed only");
//TD<D>(); // D is float here
return A<D>(static_cast<D>(_data));
}
T _data;
};