How can I get the return type of an implicit conversion of an object?
struct Bar {
operator int() const {
return 0;
}
};
// std::result_of<Bar>::type value; ???
// std::result_of<Bar::operator ??? >::type value;
I could use:
std::is_convertible<Bar, int>::value
but is_convertible is also true for float, unsigned int etc.... I would like to have the exact type.
Edit: Because my question seems unclear, why I want to know the implicit conversion type. Please think a step further to template classes. So i do not know Bar at all...
template<typename T, typename Sfinae = void>
struct ImplicitType
{
static_assert(sizeof(T) != sizeof(T), "Unknown type.");
};
template<typename T>
struct ImplicitType<T,
typename std::enable_if<std::is_convertible<T, int>::value && std::is_class<T>::value>::type>
{
using type = int;
};
template<typename T>
struct ImplicitType<T,
typename std::enable_if<std::is_convertible<T, float>::value && std::is_class<T>::value>::type>
{
using type = int;
};
struct Foo
operator float() const {
return 0.0f;
}
};
struct Bar {
operator int() const {
return 0;
}
};
ImplicitType<Foo> r; // <--- ambiguous template instantiation
ImplicitType<Bar> r; // <--- ambiguous template instantiation
For Foo I would like to get float. For Bar int.
But because I can define one or more implicit conversions for class, it gets tricky.
struct FooBar {
operator float() const {
return 0;
}
operator int() const {
return 0;
}
};
Not working live example.
So all in all it is not possible to get the right implicit conversation type of a class?
#include <iostream>
#include <typeinfo>
struct Bar {
operator int() const {
return 0;
}
operator double() const {
return 0.0;
}
struct Foo {
};
operator Foo() const {
return Foo();
}
};
int main() {
std::cout << typeid( decltype((int)Bar()) ).name();
std::cout << std::endl;
std::cout << typeid( decltype((double)Bar()) ).name();
std::cout << std::endl;
std::cout << typeid( decltype((Bar::Foo)Bar()) ).name();
std::cout << std::endl;
}
According to that fact, that function Bar::operator int() is a member function of class Bar, you guarantee, that there is a this reference for it, so that's why I provide a default object Bar() for all the stuff.
The result is:
i
d
N3Bar3FooE
Related
I have the following code:
#include <iostream>
#include <string>
#include <type_traits>
struct Foo
{
int i;
int j;
};
template<typename T, T DEFAULT>
class Bar
{
public:
Bar(): mVal(DEFAULT)
{
std::cout << "Bar constructor with mVal = " << mVal << "\n";
}
~Bar(){}
Bar(const T &i) : mVal(i)
{
std::cout << "Bar constructor with mVal = " << mVal << "\n";
}
Bar &operator=(T const &val)
{
mVal = val;
std::cout << "Bar assignment operator with mVal = " << mVal << "\n";
return *this;
}
explicit operator T() const
{
return mVal;
}
private:
T mVal;
};
int main()
{
std::cout << "Hello \n";
Bar<int, 10> bar1;
}
This is working fine in gcc C++14 as long as the first template parameter in Bar is of an integral type. If I want to do Bar<Foo, {}> the following error message is printed:
on-type template parameters of class type only available with '-std=c++2a' or '-std=gnu++2a'
I already expected that. Changing template<typename T, T DEFAULT> class Bar to template<typename T, T DEFAULT = {}> class Bar leads to the same error.
Also a template specialization template<typename T> class Bar<T, {}> does not work for the same reason.
I also tried to experiment with std::enable_if_t<std::is_integral<T>::value> but could not find a solution that would work.
Is there any possible way to just write Bar<Foo> and not have to write a separate class like template<typename T, T DEFAULT> class BarDefault and template<typename T> class Bar for it?
Template parameters and template arguments - cppreference.com
A non-type template parameter must have a structural type, which is one of the following types (optionally cv-qualified, the qualifiers are ignored):
lvalue reference type (to object or to function);
an integral type;
a pointer type (to object or to function);
a pointer to member type (to member object or to member function);
an enumeration type;
std::nullptr_t; (since C++11)
a floating-point type;
a literal class type with the following properties:
all base classes and non-static data members are public and non-mutable and
the types of all bases classes and non-static data members are structural types or (possibly multi-dimensional) array thereof. (since C++20)
So basically custom structure as template value parameter is available since c++20.
Demo
You can overcome this problem by providing depending template which job is to provide a default value:
https://godbolt.org/z/RFp_xH
#include <iostream>
#include <string>
#include <type_traits>
struct Foo
{
int i = 42;
int j = 4;
};
std::ostream& operator<<(std::ostream& out, const Foo& a)
{
return out << a.i << ',' << a.j;
}
template<typename T>
struct BarDefaultValue
{
constexpr static T value()
{
return T{};
}
};
template<>
struct BarDefaultValue<int>
{
constexpr static int value()
{
return 42;
}
};
template<typename T, typename D = BarDefaultValue<T>>
class Bar
{
public:
Bar(): mVal(D::value())
{
std::cout << "Bar constructor with mVal = " << mVal << "\n";
}
~Bar(){}
Bar(const T &i) : mVal(i)
{
std::cout << "Bar constructor with mVal = " << mVal << "\n";
}
Bar &operator=(T const &val)
{
mVal = val;
std::cout << "Bar assignment operator with mVal = " << mVal << "\n";
return *this;
}
explicit operator T() const
{
return mVal;
}
private:
T mVal;
};
int main()
{
std::cout << "Hello \n";
Bar<int> bar1;
Bar<Foo> bar2;
}
You could make a default value to supply.
template<class T>
constexpr T brace_init_value{};
Then used as:
template<typename T, T DEFAULT = brace_init_value<T> >
class Bar
{
Thanks to #Marek R for his idea.
My solution to the problem is the following code which is compiling with gcc in C++14:
#include <iostream>
#include <string>
#include <type_traits>
template <typename T>
constexpr typename std::enable_if<std::is_class<T>::value, T>::type BarDefaultValue(const int &)
{
return {};
}
template <typename T>
constexpr typename std::enable_if<std::is_integral<T>::value, T>::type BarDefaultValue(const int &ret)
{
return static_cast<T>(ret);
}
struct Foo
{
int i;
int j;
};
std::ostream& operator<<(std::ostream& out, const Foo& a)
{
return out << a.i << ',' << a.j;
}
template<typename T, int DEFAULT = 0>
class Bar
{
public:
Bar(): mVal(BarDefaultValue<T>(DEFAULT))
{
std::cout << "Bar constructor with mVal = " << mVal << "\n";
}
~Bar(){}
Bar(const T &i) : mVal(i)
{
std::cout << "Bar constructor with mVal = " << mVal << "\n";
}
Bar &operator=(T const &val)
{
mVal = val;
std::cout << "Bar assignment operator with mVal = " << mVal << "\n";
return *this;
}
explicit operator T() const
{
return mVal;
}
private:
T mVal;
};
int main()
{
std::cout << "Hello \n";
Bar<int, 10> bar1;
Bar<Foo> bar2;
}
The output of this file is:
Hello
Bar constructor with mVal = 10
Bar constructor with mVal = 0,0
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);
}
I have a template class where I try to convert a template verision to another via operator overload
enum MyTypes {A,B,C}
template<MyTypes T>
MyClass {
const static MyType type_ = T;
template<MyTypes U>
MyClass<U> convert(MyTypes t) {
MyType<U> ret = MyType<U>();
....
return r;
}
template<MyTypes U>
MyClass<U> operator()() {
return convert(U);
}
}
However, this yields (on gcc, c11)
conversion from MyClass<0u> to non-scalar type MyClass<1u> requested
removing the template functions and trying
MyClass<A> operator()() {
MyClass<A> a = MyClass<A>();
...
return a;
}
throws
the error operator cannot be overloaded
Basically, what I am trying to achieve is that if I have
MyClass<A> a = MyClass<A>;
MyClass<B> b = a;
that it creates a new MyClass based on a and the conversion. Any idea what my mistake here is?
EDIT:
I tossed out one template function, just leaving the operator
template<MyTypes U>
MyClass<U> operator()() {
MyClass<U> ret = MyClass<U>();
...
return ret;
}
but this still yields
conversion from MyClass<0u> to non-scalar type MyClass<1u> requested
when trying to do
MyClass<B> = a
The following converts the value and allows for the assignment:
#include <iostream>
#include <string>
enum MyTypes { A, B, C };
template<MyTypes T>
struct MyClass{
const static MyTypes type_ = T;
std::string history{"started as " + std::to_string(T)};
template<MyTypes U>
operator MyClass<U> () {
return {history+" then became " + std::to_string(U)};
}
};
int main()
{
MyClass<A> a;
MyClass<B> b = a;
MyClass<C> c = b;
std::cout << a.history << '\n';
std::cout << b.history << '\n';
std::cout << c.history << '\n';
}
Output:
started as 0
started as 0 then became 1
started as 0 then became 1 then became 2
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;
};
How do I get type conversion to work when cascading the type conversions?
The following code should be simple, but converison from TypeB to int requires the compiler to deduce two type conversions automatically. But it does not.
I can not simply implement
operator int() const { return val; }
on the TypeB class because this is supposed to be a template class And I can not know which type to convert to.
class TypeA {
public:
TypeA( int a ) : val( a ) {}
operator int () const { return val; }
private:
int val;
};
class TypeB {
public:
TypeB( TypeA a ) : val( a ) {}
operator TypeA () const { return val; }
// operator int() const { return val; } // Explicit conversion to int which I can not know.
private:
TypeA val;
};
void main() {
TypeA a = 9;
TypeB b = a;
int int_a = a;
TypeA a2 = b;
int int_b = b; // Compilation error:
// No suitable conversion function from 'TypeB' to 'int' exists
}
Regards
In any implicit conversion sequence you are allowed at most one implicit user-defined conversion.
You can say int int_b = static_cast<TypeA>(b);, though, to bring the number of UDCs down to one.
So you want TypeB<T> to use the user defined conversions of T?
Create a template operator U that uses SFINAE to examine the conversion operators of T and accept when U is a type T has an operator U for.
An insufficient, yet easy, way is std::is_convertible -- address of T::operator U is probably better.
This will require C++11 features to do reasonably, because you'll want to use enable_if in a default template parameter.
This is a sketchy implementation: I don't cover the target type having a constructor that takes the source type.
#include <utility>
#include <type_traits>
#include <iostream>
struct A {
operator int() { return 7; }
};
template<typename T>
struct unevaluated: std::true_type {};
template<typename T, typename U, typename=void>
struct has_user_defined_conversion:std::false_type {};
template<typename T, typename U>
struct has_user_defined_conversion<T, U,
typename std::enable_if< unevaluated<
decltype(
&T::operator U
)
>::value >::type
>: std::true_type {};
template<typename T>
struct fake {
T t;
template<typename U,
typename=typename std::enable_if<has_user_defined_conversion<T,U>::value>::type
>
operator U() { return t; }
};
int main() {
int x = fake<A>();
std::cout << x << "\n";
}