Consider the following template functions:
template <class T>
const T* DoSomething(const T& t)
{
auto& id = typeid(T);
cout << "Type is " << id.name() << ", and we have a ";
cout << "ref of one\n";
return &t;
}
template <class T>
T* DoSomething(T* t)
{
auto& id = typeid(T);
cout << "Type is " << id.name() << ", and we have a ";
cout << "pointer to one \n";
return t;
}
template <class T, template <class> class container>
T* DoSomething(const container<T>& t)
{
auto& type_id = typeid(T);
auto& container_id = typeid(container<T>);
cout << "Type is " << type_id.name() << ", and we have a ";
cout << container_id.name() << "of one\n";
return t.get();
}
template <class T, template <class,class> class container, template <class> class deleter = default_delete>
T* DoSomething(const container<T, deleter<T>>& t)
{
auto& type_id = typeid(T);
auto& container_id = typeid(container<T,deleter<T>>);
cout << "Type is " << type_id.name() << ", and we have a ";
cout << container_id.name() << "of one\n";
return t.get();
}
The goal is to be able to pass to them either a plain reference, pointer, or smart pointer, and use overloading and template specilization in order for the correct function to be called. The following driving code works as expected:
char r('r');
DoSomething(r);
DoSomething(&r);
shared_ptr<char> s(new char ('s'));
unique_ptr<char> u(new char ('u'));
DoSomething(s);
DoSomething(u);
However, consider what happens if we try this:
vector<int> v {1,2};
DoSomething(v);
Now, we we get a compile error. The version of DoSomething that the compiler decides to use is the 4th one. Inside this, we reference a function get(), which vector does not have. If the compiler were to somehow pick the 1st defintion of DoSomething, it would compile fine, and work as I intend.
So can I restrict the 3rd and 4th specializations to only be matched when the template template parameter contains a get() method? Is there a way I can get this to happen, perhaps using traits, SFINAE, or some other more advanced template technique?
The version of DoSomething that the compiler decides to use is the 4th one.
Because std::vector<T, std::allocator<T>> is an exact match for the template parameter container and std::allocator<T> is an exact match for the template parameter deleter, and const container<T, deleter<T>>& is more specialized than const T& so the 4th overload is chosen as the best match by the partial ordering rules for function templates.
So can I restrict the 3rd and 4th specializations to only be matched when the template template parameter contains a get() method?
Yes, you can tell the compiler that the function returns whatever t.get() returns:
template <class T, template <class> class container>
auto DoSomething(const container<T>& t) -> decltype(t.get())
{
auto& type_id = typeid(T);
auto& container_id = typeid(container<T>);
cout << "Type is " << type_id.name() << ", and we have a ";
cout << container_id.name() << "of one\n";
return t.get();
}
If t.get() is not a valid expression then template argument deduction fails, because the argument T cannot be substituted into the function signature successfully, and so the function will not be a viable overload, and the 1st overload will be used instead.
The OP code has a flaw when a std::unique_ptr with a custom deleter is used:
struct Deleter {
void operator()(char*) const {}
};
unique_ptr<char, Deleter> u(new char ('u'), Deleter());
DoSomething(u) // The first is applied.
Using duck typing via SFINAE is troublesome.
template <typename T, typename U>
struct X {
// Is this a get like a get of a smart pointer !?
T* get();
}
I recommend the solution of user1095108.
I'd try this:
template <typename>
struct is_smart : ::std::false_type
{
};
template <typename T>
struct is_smart<::std::shared_ptr<T> > : ::std::true_type
{
};
Use like this:
::std::cout << is_smart<container<T> >{} << ::std::endl;
But I'd rather override like this:
template <class T>
T* DoSomething(const ::std::shared_ptr<T>& t)
{
}
for every container and smart pointer type you intend to support.
Related
Consider this function:
template<typename T>
void f(T c) {
std::cout<<c<<std::endl;
}
You see that it will not compile for types which does not have an operator<< overload.
Now I want to write a function that acts like a fallback for this case.
/*Fallback*/
template<>
void f(T c) {
std::cout<<"Not Printing"<<std::endl;
}
How must this function be defined to do the job?
Pre-C++20
To have these overloads work in a fallback way, we can start by defining a trait that detects the validity of the expression involving operator <<
namespace detail {
template<typename T, typename = void>
struct streamable : std::false_type{};
template<typename T>
struct streamable<T, decltype(std::declval<std::ostream&>() << std::declval<T&>(), void())> : std::true_type {};
}
It's just your typical use of the detection idiom with as little extra library support as possible. Depending on the standard you are building against, this may be written in other ways (for instance std::void_t can be used, if available).
Now, the two overloads can be specified rather simply:
template<typename T>
auto f(T c) -> std::enable_if_t<detail::streamable<T>::value, void> {
std::cout<<c<<std::endl;
}
template<typename T>
auto f(T c) -> std::enable_if_t<!detail::streamable<T>::value, void> {
/// other code
}
Post C++20, concepts and constraints make it a whole lot easier. It can even be written ad-hoc:
template<typename T>
requires requires(std::ostream& os, T& c) { os << c; }
void f(T c) {
std::cout<<c<<std::endl;
}
template<typename T> // No extra step, subsumed by the above when possible
void f(T c) {
// other code
}
With concepts (C++20), we can achieve this like so:
template<typename T>
concept Streamable = requires(T t){std::declval<std::ostream&>() << t; };
template<Streamable T>
void f(T c) { std::cout << c << std::endl; }
/*Fallback*/
template<typename T>
void f(T c) { std::cout << "fallback" < <std::endl; }
Demo
Test:
struct Foo{};
int main()
{
Foo foo;
f(foo); // prints "fallback"
int a = 42;
f(a); // prints "42"
}
If you want to make doubly sure that your fallback will only happen if your type is not Streamable, you can constrain it, too:
template<typename T> requires (!Streamable<T>)
void f(T c) { /*...*/ }
You have several options of doing this. Arguably the most elegant way is to define your own type trait (similar to the ones in type_traits).
Let's define a is_streamable type trait. It takes two template arguments: S is the data type of the file stream (e.g. std::ostream or std::fstream or any other type that defines a custom streaming operator that is compatible with T) and secondly the data type of the object to be streamed into this file stream T:
template<typename S, typename T, typename = void>
struct is_streamable : std::false_type {
};
template<typename S, typename T>
struct is_streamable<S, T, decltype(std::declval<S&>() << std::declval<T&>(), void())> : std::true_type {
};
So far this type trait compiles with C++11 and onwards. For C++14 and later we can create a convenient alias for it similar to other type traits in C++17:
template <typename S, typename T>
static constexpr is_streamable_v = is_streamable<S,T>::value;
This type trait will now be the basis for the next step which will make use of SFINAE (C++11 onwards), constexpr if (C++17 onwards) or concepts (C++20).
In C++11 you could achieve this with either by putting the different implementations into partial specialisations of the same struct and call it with a helper function:
class f_imp {
};
template <typename T>
class f_imp<T,true> {
public:
static constexpr void imp(T c) {
std::cout << "streamable: " << c << std::endl;
}
};
template <typename T>
class f_imp<T,false> {
public:
static constexpr void imp(T c) {
std::cout << "not streamable" << std::endl;
}
};
template <typename T>
void f(T c) {
return f_imp<T,is_streamable<std::ostream,T>::value>::imp(c);
}
Try it here!
Alternatively you could apply SFINAE either by adding a second input parameter or applying it to the return type:
template<typename T, typename std::enable_if<is_streamable<std::ostream,T>::value>::type* = nullptr>
void f(T t) {
std::cout << "streamable" << std::endl;
}
template<typename T, typename std::enable_if<!is_streamable<std::ostream,T>::value>::type* = nullptr>
void f(T t) {
std::cout << "not streamable" << std::endl;
}
Try it here!
In C++17 you can actually use a constexpr if to avoid adding a second template argument and overloading of the function altogether. You can insert all the code inside the function and use if constexpr in combination with std::is_same_v and our is_streamable_v to decide at compile time which branch of our code each template type should take. This is in particular convenient if adding two specialisations would result in duplicate code but it might be harder to read.
template<typename T>
void f(T c) {
if constexpr (is_streamable_v<std::ostream,T>) {
std::cout << "streamable:" << c << std::endl;
} else {
// Fallback
std::cerr << "not streamable" << std::endl;
}
return;
}
Try it here!
Finally in C++20 you could use this type trait to define a concepts such as streamable and not_streamable:
template <typename T>
concept streamable = is_streamable_v<std::ostream,T>;
template <typename T>
concept not_streamable = !streamable<T>;
Then you can go on to apply them to your two overloads of the functions
template <streamable T>
void f(T c) {
std::cout << "streamable: " << c << std::endl;
}
template <not_streamable T>
void f(T c) {
std::cout << "not streamable" << std::endl;
}
Try it here!
Be aware that you will have to also apply the same logic to any custom streaming operator of a templated class, e.g. of a templated vector. Instead of declaring the operator for any template parameter typename T you would have to only declare it for streamable element types only. In C++20 for example with said streamable concept:
template <streamable T>
std::ostream& operator << (std::ostream& os, std::vector<T> const& vec) {
for (auto const& v: vec) {
os << v << " ";
}
return os;
}
Otherwise - as the template argument to the is_streamable operator is std::vector<T> as a whole - the compiler sees the operator << for std::vector<T> without checking if it would result in a compilation error for an unstreamable type T which does not define the operator << itself.
Try it here!
I am writing a custom console in C++ in Windows which will hopefully print out any object or at least handle every object.
Here is my WriteLine method
template<typename T>
inline void WriteLine(const T& t)
{
std::cout << t << "\n";
}
I have an base "object" class which has an overloaded << operator as shown
friend std::ostream& operator<<(std::ostream& stream, const object& object)
{
stream << object.toString();
return stream;
}
All my classes should derive from this. However, is their a way to check if a class has an overloaded << operator and if not handle it, here's what I mean.
(pseudo code)
template<typename T>
inline void WriteLine(const T& t)
{
//check if the object has an overloaded << operator
if(itdoes){
//then print as normal
std::cout << t;
}
else {
//if it hasn't been overloaded, just print the type of the object using <typeinfo>
std::cout << typeid(T).name()
}
}
In a nutshell I want the code to compile and handle the case when a type (T) has not been overloaded, rather than thrown a compile error. If it hasn't been overloaded just print out the objects type name
Thanks
You should be able to do this using SFINAE and tag dispatch:
template <class T, class = void>
struct HasInserter :
std::false_type
{};
template <class T>
struct HasInserter<T, std::void_t<decltype(std::declval<std::ostream&>() << std::declval<const T>())>> :
std::true_type
{};
template <class T>
void WriteLine(const T &t, std::true_type)
{
std::cout << t << '\n';
}
template <class T>
void WriteLine(const T &t, std::false_type)
{
std::cout << typeid(T).name() << '\n';
};
template <class T>
void WriteLine(const T &t)
{
WriteLine(t, HasInserter<T>{});
}
This creates a helper trait which uses SFINAE to distinguish whether the expression stream << t is well-formed. The trait is then used by the WriteLine function for tag dispatch.
Note that std::void_t is a C++17 feature, but it's trivial to implement in earlier versions of the standard, so the above solution can be made to work in C++11.
As #mdatsev pointed out in comments, if you're using Boost, you don't have to implement the trait yourself and you can use boost::has_left_shift<std::ostream&, const T> instead (with boost::true_type substituted for std::true_type, likewise with false).
I've got following class:
class Foo {
public:
template <typename T>
T bar() {
cout << "Called with return type: " << typeid(T).name() << endl;
T t = //... (some implementation here)
return t;
}
}
It's invoked in following way:
Foo foo;
int i = foo.bar<int>();
long l = foo.bar<long>();
Now i'd like to have different specialization for cases when function is invoked with shared_ptr<T>
Foo foo;
foo.bar<shared_ptr<int>>();
foo.bar<shared_ptr<long>>();
But of course I don't want to create full specialization for each type. Is it possible to implement such behaviour (can be trait-based if required)?
You cannot partially specialize functions. For a story on why, check out this GOTW.
You can partially specialize classes though, so what you could do is:
template <typename T>
T bar() {
return bar_impl<T>::apply(this);
}
Where:
template <typename T>
struct bar_impl {
static T apply(Foo* ) {
// whatever
}
}
template <typename T>
struct bar_impl<std::shared_ptr<T>> {
static std::shared_ptr<T> apply(Foo* ) {
// whatever else
}
}
There's certainly many ways to do it. The first way that comes to my mind is simply function overloading. Since you don't have a parameter to overload on, you'll have to make one. I like pointers, which effectively act as a way to pass types to functions.
class Foo {
//regular overload
template<typename T>
T bar(T*) { //takes a pointer with an NULL value
cout << "Called with return type: " << typeid(T).name() << endl;
T t = //... (some implementation here)
return t;
}
//shared_ptr overload - NOTE THAT T IS THE POINTEE, NOT THE SHARED_PTR
template<typename T>
std::shared_ptr<T> bar(std::shared_ptr<T>*) { //takes a pointer with an null value
cout << "Called with return type: " << typeid(T).name() << endl;
std::shared_ptr<T> t = //... (some implementation here)
return t;
}
public:
template <typename T>
T bar() {
T* overloadable_pointer = 0;
return bar(overloadable_pointer);
}
};
I've never heard of anyone else using pointers to pass types around, so if you choose to do this, comment thoroughly, just to be safe. It is wierd code.
It may be more intuitive to simply use a helper struct to do template specialization, which is what most people would do. Unfortunately, if you need access to the members of Foo (which you presumably do), using template specialization would require you to pass all those members to the function, or friend the template helpers. Alternatively, you could pass a type_traits specialization thing to another member, but that ends up simply being a complex version of the pointer trick above. Many find it more normal and less confusing though, so here's that:
template<typename T>
struct Foo_tag {};
class Foo {
//regular overload
template<typename T>
T bar(Foo_tag<T>) {
}
//shared_ptr overload - NOTE THAT T IS THE POINTEE, NOT THE SHARED_PTR
template<typename T>
std::shared_ptr<T> bar(Foo_tag<std::shared_ptr<T>>) {
}
public:
template <typename T>
T bar() {
return bar(Foo_tag<T>{});
}
}
Since noone proposed it yet, one can use SFINAE to distinguish between T and std::shared_ptr<U>:
template <typename T>
struct is_shared_ptr_impl : std::false_type {};
template <typename T>
struct is_shared_ptr_impl<std::shared_ptr<T>> : std::true_type {};
template <typename T>
using is_shared_ptr = typename is_shared_ptr_impl<typename std::decay<T>::type>::type;
class Foo
{
public:
template <typename T>
auto bar()
-> typename std::enable_if<!is_shared_ptr<T>{}, T>::type
{
std::cout << "T is " << typeid(T).name() << std::endl;
return {};
}
template <typename T>
auto bar()
-> typename std::enable_if<is_shared_ptr<T>{}, T>::type
{
using U = typename std::decay<T>::type::element_type;
std::cout << "T is shared_ptr of " << typeid(U).name() << std::endl;
return {};
}
};
DEMO
class trytemplate
{
public:
//////// 1
template <typename T>
trytemplate(const T& p)
{
std::cout << p << std::endl;
}
//////// 2
template <typename U>
trytemplate(const std::vector<U>& p)
{
std::cout << p[0] << " " << p.size() << std::endl;
}
//////// 3
template <typename U, typename V>
trytemplate(const V<U>& p)
{
std::cout << p[0] << " " << p.size() << std::endl;
}
};
ctor 2 works fine, but I'd like to make it something like 3 (3 doesnt compile).
So that I can do something like:
int i = 123;
trytemplate o1(i); // call ctor 1
std::vector<float> v1(1, 123.0f);
trytemplate o2(v1); // ideally can be deduced by compiler, and call ctor 3
MyVector<float> v2(1, 123.0f);
trytemplate o3(v2); // ideally can be deduced by compiler, and call ctor 3
In such case I can pass in any vector-like container, just making sure that class has operator[] and size().
So the question is: is it possible to make ctor like number 3?
Or is there any better approach?
P.S. If anyone could suggest a better title then I would change it, thanks!
Use a template template parameter
template <template<typename> class V, typename U>
trytemplate(const V<U>& p)
{
std::cout << p[0] << " " << p.size() << std::endl;
}
You can also add in variadic templates to accept class templates that take more than one template parameters.
template <template<typename...> class V, typename... Params>
trytemplate(const V<Params...>& p)
{
std::cout << p[0] << " " << p.size() << std::endl;
}
Note that if you use the non-variadic (first) version then the class template you pass in should only take a single template argument. This means it cannot be used with std::vector because it takes a second template argument, the allocator type (which has a default argument of std::allocator<T>). If your compiler doesn't support variadic templates, like VS2012 without Nov CTP, then use this
template <template<typename, typename> class V, typename U, typename A>
trytemplate(const V<U, A>& p)
{
std::cout << p[0] << " " << p.size() << std::endl;
}
consider the code
template <class A>
class B;
template <class A>
class B<const A>{};
template <class A, int N>
class B<A[N]>{};
template <class A>
class B<A*>{};
template <class A>
class B<A&>{};
The following template instantiations work fine:
A<int*&>
A<const int*>
A<int*[3]>
but the following one doesn't work:
A<const int[3]>
Is there some reason that this particular combination is invalid or is it perhaps a bug with g++4.6.3?
By the way I managed to get around this using SFINAE and boost::disable_if<>, so at least the problem is solved.
EDIT
I forgot to mention that the error in question is an ambiguous class template instantiation and it couldn't decide between the overload for const or the overload for an array.
EDIT2
This has nothing to do with pointers, here's the full context:
I'm going through the book C++ Template Metaprogramming and am doing question 2-3 (Chapter 2 question 3) which says:
Use the type traits facilities to implement a type_descriptor class template, whose instances, when streamed, print the type of their template parameters:
NOTE: we cannot use RTTI to the same effect since, according to 18.5.1 [lib.type.info] paragraph 7 of the standard, typeid(T).name() is not guaranteed to return a meaningful result.
My solution (including the the workaround for the compilation error) is as follows:
//QUESTION 2-3
template <class T, class enable = void>
struct type_descriptor
{
std::string operator()() const
{
return "Unknown";
}
};
//specializations for primitive types
#define TYPE_DESC_SPEC(type) template <> \
struct type_descriptor<type,void> \
{std::string operator()() const{return #type;}};
TYPE_DESC_SPEC(int)
TYPE_DESC_SPEC(long)
TYPE_DESC_SPEC(void)
TYPE_DESC_SPEC(short)
TYPE_DESC_SPEC(unsigned char)
TYPE_DESC_SPEC(unsigned short)
TYPE_DESC_SPEC(unsigned long)
//specializations for modifiers *, const, &, and [N]
template <class T>
struct type_descriptor<T&,void>
{std::string operator()(){return type_descriptor<T>()() + " &";}};
template <class T>
struct type_descriptor<T*,void>
{std::string operator()(){return type_descriptor<T>()() + " *";}};
//Replace void with what's in the comment for the workaround.
template <class T>
struct type_descriptor<const T, void/*typename boost::disable_if<boost::is_array<T> >::type*/>
{std::string operator()(){return type_descriptor<T>()() + " const";}};
template <class T>
struct type_descriptor<T(*)(),void>
{std::string operator()(){return type_descriptor<T>()() + " (*)()";}};
template <class T, class U>
struct type_descriptor<T(*)(U),void>
{std::string operator()(){return type_descriptor<T>()() + " (*)(" + type_descriptor<U>()() + ")";}};
template <class T, int N>
struct type_descriptor<T[N],void>
{
std::string operator()()
{
std::stringstream s;
s << type_descriptor<T>()() << " [" << N << "]";
return s.str();
}
};
template <class T>
struct type_descriptor<T[],void>
{std::string operator()(){return type_descriptor<T>()() + " []";}};
//Now overload operator<< to allow streaming of this class directly
template <class T>
std::ostream & operator<<(std::ostream & s, type_descriptor<T> t)
{
return s << t();
}
//END QUESTION 2-3
Sample usage is:
std::cout << "\nQuestion 2-3 results\n";
std::cout << type_descriptor<int*>() << std::endl;
std::cout << type_descriptor<int*[3]>() << std::endl;
std::cout << type_descriptor<std::string*>() << std::endl;
std::cout << type_descriptor<const int&>() << std::endl;
std::cout << type_descriptor<const int *const&>() << std::endl;
std::cout << type_descriptor<int[4]>() << std::endl;
std::cout << type_descriptor<int(*)()>() << std::endl;
std::cout << type_descriptor<int*&(*)(const char &)>() << std::endl;
std::cout << type_descriptor<int*&>() << std::endl;
std::cout << type_descriptor<int[]>() << std::endl;
std::cout << type_descriptor<const long[]>() << std::endl;
and the corresponding output is (when the workaround is in, otherwise it doesn't compile on that last one):
int *
int * [3]
Unknown *
int const &
int const * const &
int [4]
int (*)()
int * & (*)(Unknown const &)
int * &
int []
long const []
So C++ is able to differentiate pointers and arrays for the template parameters, is able to correctly, recursively, separate compound types and output the correct result, except for const A[]. It needs help with that one
An array type with a const element type is both a const qualified type (the const applies bidirectionally) and an array type.
So you should fix the specializations.