type trait for function pointer? - c++

I need to conditionally use either std::abs or std::fabs inside template class, here is relevant code in simplified version:
template <typename T>
class C
{
public:
using type = std::conditional_t<std::is_integral_v<T>, std::uint64_t, long double>;
using check = std::is_integral<type>;
// ERROR: mismatch in format parameter list
constexpr auto ptr_abs = check::value ? &std::abs<check::value_type> : &std::fabs;
// use pointer
void use_ptr()
{
auto x = (*ptr_abs)(-3);
}
};
None of the attempts worked for me, I'm clueless.
int main()
{
C<int> a;
a.f();
C<float> b;
b.f();
}

Do you really need to work with function pointers? Wouldn't be better to exploit C++ type-safe mechanisms? Such as follows:
template <typename T>
class C
{
public:
using type = std::conditional_t<std::is_integral_v<T>, std::uint64_t, long double>;
static const bool check = std::is_integral_v<type>;
std::function<type(type)> abs = [](auto arg)
{
if constexpr (check) return std::abs(static_cast<long long>(arg));
else return std::fabs(arg);
};
void use()
{
auto x = abs(-3);
}
};
This works for me well. Just note that there is no std::abs for unsigned integers, therefore, to avoid ambiguity, I had to choose a particular overload by casting (to long long in this example; I don't know what is Result).
Before C++17, where there is no if constexpr, you can achieve the same just with some more typing by using template specializations.

Resolve the function overload with the type of the pointer:
#include <cmath>
#include <type_traits>
#include <cstdlib>
#include <iostream>
template <typename T>
class C {
public:
static constexpr T (*ptr_abs)(T) = &std::abs;
void f() {
std::cout << typeid(ptr_abs).name() << "\n";
auto x = (*ptr_abs)(-3);
}
};
int main()
{
C<int> a;
a.f(); // PFiiE
C<float> b;
b.f(); // PFffE
C<double> c;
c.f(); // PFddE
}

Maybe I've misunderstood your problem, but it seems to me that you could separately define your version of abs that behaves as you want and then use it inside other classes
#include <cmath>
#include <cstdint>
#include <complex>
#include <iostream>
#include <limits>
#include <type_traits>
#include <typeinfo>
namespace my {
template <class T>
auto abs_(T x)
{
if constexpr ( std::is_unsigned_v<T> ) {
return static_cast<uintmax_t>(x);
}
else if constexpr ( std::is_integral_v<T> ) {
return static_cast<uintmax_t>(std::abs(static_cast<intmax_t>(x)));
}
else {
return std::fabs(static_cast<long double>(x));
}
}
template <class T>
auto abs_(std::complex<T> const& x)
{
return std::abs(static_cast<std::complex<long double>>(x));
}
}
template <typename T>
class C
{
public:
void use(T x)
{
std::cout << typeid(T).name() << ' ' << x;
auto a = my::abs_(x);
std::cout << ' ' << typeid(a).name() << ' ' << a << '\n';
}
};
int main()
{
C<int> a;
a.use(-42);
C<float> b;
b.use(-0.1);
C<long long> c;
c.use(std::numeric_limits<long long>::min());
C<size_t> d;
d.use(-1);
C<std::complex<double>> e;
e.use({-1, 1});
}
Testable here.

Related

Template function deduction fail on std::conditional argument

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

Why can't the compiler deduce the return type if argument type depends on a template parameter

I am getting a template deduction error when trying to choose a function of an overload set (foo) within a template based on another template parameter:
#include <iostream>
#include <string>
void foo(int a){
std::cout << "int";
}
void foo(double a) {
std::cout << "double";
}
template <typename T, typename R>
void print(const T& data, R(*fun)(typename T::type) ) {
fun(data.value);
}
struct IntData{
using type = int;
type value;
};
int main()
{
IntData x{1};
print(x, foo);
}
I get the following error:
In function 'int main()':
27:15: error: no matching function for call to 'print(IntData&, <unresolved overloaded function type>)'
27:15: note: candidate is:
15:6: note: template<class T, class R> void print(const T&, R (*)(typename T::type))
15:6: note: template argument deduction/substitution failed:
27:15: note: couldn't deduce template parameter 'R'
As template deduction should proceed from left to right, my assumption was that once the type of T is deduced, the type of R should also be deducable. Actually, I can get rid of the error when calling print like
print<IntData>(x, foo);
which seems to show that R can actually be deduced once T is known. So why doesn't it work if both parameter should be deduced?
Thank you!
I believe it is because you have R as the return type for your function pointer argument.
Note this quote from a previous question
So, when we ask about the signature of a function, we have to give two answers:
For functions that are specializations of function templates, the
signature includes the return type.
For functions that are not specializations, the return type is not part of the signature.
Since foo is simply an overloaded function and void is not part of the foo function signature, R will not assist the compiler in deducing correct function overload. Therefore, the use of foo as a function pointer is ambiguous within the scope of main. The compiler usually resolves the overload by matching the types of the provided arguments, for which there are none when the function pointer is by itself.
I believe this is the most robust solution, to include an intermediary function to resolve the previously ambiguous function pointer. I included some other types in addition to int to demonstrate the flexibility of using auto with the strategies mentioned below.
#include <iostream>
#include <string>
void foo(int a){
std::cout << "int" << std::endl;
}
void foo(double a) {
std::cout << "double" << std::endl;
}
bool foo(char a) {
std::cout << "char" << std::endl;
return true;
}
template <typename T, typename R>
R print(const T& data, R(*fun)(typename T::type) ) {
return fun(data.value);
}
struct IntData{
using type = int;
type value;
};
struct DoubleData{
using type = double;
type value;
};
struct CharData{
using type = char;
type value;
};
template <typename T>
auto print2(const T& data)
{
auto(*fooResolved)(typename T::type) = foo;
return print(data,fooResolved);
}
int main()
{
IntData x{1};
print2(x);
DoubleData y{1.0};
print2(y);
CharData z{'a'};
bool result = false;
std::cout << "bool before: " << result << std::endl;
result = print2(z);
std::cout << "bool after : " << result << std::endl;
}
Here are a few more potential solutions to help illustrate the problem:
(note the change is removing R as the second template argument)
#include <iostream>
#include <string>
void foo(int a){
std::cout << "int";
}
void foo(double a) {
std::cout << "double";
}
template <typename T>
void print(const T& data, void(*fun)(typename T::type) ) {
fun(data.value);
}
struct IntData{
using type = int;
type value;
};
int main()
{
IntData x{1};
print(x, foo);
}
As well as this (passing the value directly, which allows for multiple return types)
#include <iostream>
#include <string>
void foo(int a){
std::cout << "int";
}
void foo(double a) {
std::cout << "double";
}
template <typename T, typename R>
void print(const T& data, R (*fun)(T) ) {
fun(data);
}
struct IntData{
using type = int;
type value;
};
int main()
{
IntData x{1};
print(x.value, foo);
}
And to further illustrate the original issue (see the return type is now deduced)
#include <iostream>
#include <string>
void foo(int a){
std::cout << "int" << std::endl;
}
bool foo(double a) {
std::cout << "double" << std::endl;
return true;
}
template <typename T, typename R>
R print(const T& data, R (*fun)(T) ) {
return fun(data);
}
struct IntData{
using type = int;
type value;
};
struct DoubleData{
using type = double;
type value;
};
int main()
{
IntData x{1};
print(x.value, foo);
//foo(int) does not return a value
//bool test = print(x.value, foo); // Does not compile
DoubleData y{1.0};
bool result = false;
result = print(y.value, foo);
std::cout << result << std::endl;
}
And while we're at it, you could also resolve them given the original code by explicitly specifying which foo you want
#include <iostream>
#include <string>
void foo(int a){
std::cout << "int";
}
void foo(double a) {
std::cout << "double";
}
template <typename T, typename R>
void print(const T& data, R(*fun)(typename T::type) ) {
fun(data.value);
}
struct IntData{
using type = int;
type value;
};
int main()
{
IntData x{1};
void(*fooResolved)(int) = foo;
print(x, fooResolved);
}

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.

Why is the constructor in this C++ code ambiguous and how do I fix it?

In the below code, the compiler can't figure out which constructor I want to use. Why, and how do I fix this? (Live example)
#include <tuple>
#include <functional>
#include <iostream>
template<typename data_type, typename eval_type, typename Type1, typename Type2>
class A
{
public:
using a_type = std::tuple<Type1, Type2>;
using b_type = std::tuple<std::size_t,std::size_t>;
inline explicit constexpr A(const std::function<data_type(a_type)>& Initializer,
const std::function<eval_type(data_type)>& Evaluator,
const Type1& elem1, const Type2& elem2)
{
std::cout << "idx_type" << std::endl;
}
inline explicit constexpr A(const std::function<data_type(b_type)>& Initializer,
const std::function<eval_type(data_type)>& Evaluator,
const Type1& elem1, const Type2& elem2)
{
std::cout << "point_type" << std::endl;
}
};
int main()
{
int a = 1;
long long b = 2;
auto c = A<double, double, long long, int>{
[](std::tuple<long long,int> p)->double { return 1.0*std::get<0>(p) / std::get<1>(p); },
[](double d)->double { return d; }, b,a
};
return 0;
}
The reason it doesn't work is because a lambda is not a std::function and so the compiler tries to create one using the fifth overload of the constructor. The problem is that both of your A constructors can be used because of this conversion and the reason that std::tuple<long long,int> and std::tuple<std::size_t,std::size_t> are constructible from each other makes this ambigious for the compiler what constructor to pick.
What you could do is explicitly cast to the desired std::function (MCVE of #PasserBy used in comments), like this:
#include <tuple>
#include <functional>
#include <iostream>
template<typename data_type, typename Type1, typename Type2>
class A
{
public:
using a_type = std::tuple<Type1, Type2>;
using b_type = std::tuple<std::size_t,std::size_t>;
A(const std::function<data_type(a_type)>&)
{
std::cout << "idx_type" << std::endl;
}
A(const std::function<data_type(b_type)>&)
{
std::cout << "point_type" << std::endl;
}
};
int main()
{
std::function<double(std::tuple<long long, int>)> func = [](auto p) -> double { return 1; };
auto c = A<double, long long, int>{
func
};
}
As #SombreroChicken mentioned, std::function<R(Args...)> has a constructor that allows any callable object c to initialize it, as long as c(Args...) is valid and returns something convertible to R.
To fix it, you may use some SFINAE machinery
#include <tuple>
#include <functional>
#include <iostream>
#include <type_traits>
template<typename data_type, typename Type1, typename Type2>
class A
{
template<typename T>
struct tag
{
operator T();
};
public:
using a_type = std::tuple<Type1, Type2>;
using b_type = std::tuple<std::size_t,std::size_t>;
template<typename C, std::enable_if_t<std::is_invocable_v<C, tag<b_type>>>* = nullptr>
A(C&& initializer)
{
std::cout << "size_t" << std::endl;
}
template<typename C, std::enable_if_t<std::is_invocable_v<C, tag<a_type>>>* = nullptr>
A(C&& initializer)
{
std::cout << "other" << std::endl;
}
};
int main()
{
auto c = A<double, long long, int>{
[](std::tuple<long long, int> p) -> double { return 1; }
};
auto c2 = A<double, long long, int>{
[](std::tuple<std::size_t, std::size_t>) -> double { return 2; }
};
}
Live
Here, we turn off the constructor if the callable can be called with b_type or a_type respectively. The extra indirection through tag is there to disable the conversion between tuples of different types

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