Templates, decltype and non-classtypes

I have a function definition like this:

template <typename T> auto print(T t) -> decltype(t.print()) { return t.print(); } 

The idea is that the argument must be of type T and must have a print function. This print function can return anything, explaining the need for decltype . So, for example, you can do:

 struct Foo { int print() { return 42; } }; struct Bar { std::string print() { return "The answer..."; } }; ... std::cout << print(Foo()) << std::endl; std::cout << print(Bar()) << std::endl; /* outputs: 42 The answer... */ 

I read that templates cannot create instances of the runtime and that you can get classes from a base class and then determine their types to see which template argument to use. However, how would I do this for a non-class ? The idea is to have:

 template <typename T> T print(T t) { return t; } 

but it gives me ambiguous overload errors. Qualification does not work, i.e. print<Foo> . And the other got a 'cha, that if I had a functor like:

 struct Foo { virtual int print(); operator int() const { return 42; } }; 

How to solve it now?

So my question is, can all of these ambiguities be resolved using templates, or do I need to write a bunch of redundant code?

Test

I gradually added tests, copying / pasting each editable solution from below. Here are the results:

With the following classes:

 struct Foo { int print() { return 42; } operator int() const { return 32; } }; struct Bar { std::string print() { return "The answer..."; } operator int() const { return (int)Foo(); } }; struct Baz { operator std::string() const { return std::string("The answer..."); } }; 

And the following test result:

 std::cout << print(Foo()) << std::endl; std::cout << print(Bar()) << std::endl; std::cout << print(42) << std::endl; std::cout << print((int)Foo()) << std::endl; std::cout << print("The answer...") << std::endl; std::cout << print(std::string("The answer...")) << std::endl; std::cout << print((int)Bar()) << std::endl; std::cout << print((std::string)Baz()) << std::endl; 

Both are correctly output:

 42 The answer... 42 32 The answer... The answer... 32 The answer... 
+3
source share
2 answers

You can use the following approach, which calls the print() member function on the input, if such a member function exists, otherwise it will return the input itself:

 namespace detail { template<typename T, typename = void> struct print_helper { static T print(T t) { return t; } }; template<typename T> struct print_helper<T, decltype(std::declval<T>().print(), (void)0)> { static auto print(T t) -> decltype(t.print()) { return t.print(); } }; } template<typename T> auto print(T t) -> decltype(detail::print_helper<T>::print(t)) { return detail::print_helper<T>::print(t); } 

Here is a living example .

+5
source

A simple solution using manual overloading for each type you want to print directly:

Define the first implementation that calls T::print() . Use overloading to specify alternative implementations for all types that do not have this function. I do not recommend this solution, but it is very easy to understand.

 template<typename T> auto print(T t) -> decltype(t.print()) { return t.print(); } int print(int t) { return t; } std::string print(std::string t) { return t; } // ... and so on, for each type you want to support ... 

A more advanced solution using SFINAE , which automatically uses T::print() if and only if it is:

First, define a sign that can decide if your type has a print() function. In principle, this attribute is inherited from std::true_type or std::false_type , depending on the decision made in some auxiliary class ( _test_print ). Then use this type in the enable_if compilation decision, which defines only one of the two cases and hides the other (so it does not overload).

 // Type trait "has_print" which checks if T::print() is available: struct _test_print { template<class T> static auto test(T* p) -> decltype(p->print(), std::true_type()); template<class> static auto test(...) -> std::false_type; }; template<class T> struct has_print : public decltype(_test_print::test<T>(0)) {}; // Definition of print(T) if T has T::print(): template<typename T> auto print(T t) -> typename std::enable_if<has_print<T>::value, decltype(t.print())>::type { return t.print(); } // Definition of print(T) if T doesn't have T::print(): template<typename T> auto print(T t) -> typename std::enable_if<!has_print<T>::value, T>::type { return t; } 

Watch a live demo .

+2
source

Source: https://habr.com/ru/post/944912/


All Articles