I'm looking for a solution for the following problem: I have a class in which I want to overload an operator (in this example &) for all types of pointers and for all types of arrays. Inside the implementation for arrays I need to have access to the arraysize and inside the implementation for pointers I must be able to do something with the dereferenced object.
As pointed out here, the way for the arrays is quite clear:
template<typename T, unsigned int N>
void operator&(T (&arr)[N])
{
cout << "general array operator: " << N << "\r\n";
}
But for the pointers neither of the following works:
// if I use this, the operator gets ambigous for arrays
template<typename T>
inline void operator&(T* p)
{
cout << "general pointer operator: " << (*p) << "\r\n";
}
// this doesn't work because one cannot dereference void*
void operator&(void* p)
{
cout << "general pointer operator\r\n";
(*this) & (*p);
}
Is there any good and clean solution to achieve different behaviour of an operator for arbitrary arrays and arbitrary pointers?
Here is a complete example code:
#include <iostream>
struct Class
{
template<typename T>
void operator&(T* p)
{
std::cout << "general pointer operator" << (*p) << std::endl;
}
template<typename T, unsigned int N>
void operator&(T (&arr)[N])
{
std::cout << "general array operator" << N << std::endl;
}
};
int main()
{
int myarr[5];
int* p = myarr;
Class obj;
obj & myarr; // error: operator is ambigous
obj & p; // works
return 0;
}
I have to admit that I have no idea why your snippet fails to compile properly. Anyway, a good old tag dispatching workaround seems to be working.
class cClass
{
public:
template<class T, size_t N>
void impl(T (&x)[N], std::true_type)
{
cout << "general array operator" << N << '\n';
}
template<typename T>
void impl(T* p, std::false_type)
{
cout << "general pointer operator" << (*p) << '\n';
}
template<typename T>
void operator&(T && x)
{
impl( std::forward<T>(x), std::is_array< typename std::remove_reference<T>::type >() );
}
};
The solution that changes the least code is:
template<typename T>
void operator&(T*const& p)
which gets rid of the ambiguity. I'd go with tag dispatching myself.
A C++98 solution is to have the pointer-taking operator take a const reference to a pointer.
#include <iostream>
struct Class
{
template<typename T>
void operator&(T* const &p)
{
std::cout << "general pointer operator " << (*p) << std::endl;
}
template<typename T, unsigned int N>
void operator&(T (&)[N])
{
std::cout << "general array operator " << N << std::endl;
}
};
int main()
{
int myarr[1] = { 2 };
int* p = myarr;
Class obj;
obj & myarr;
obj & p;
return 0;
}
Output:
general array operator 1
general pointer operator 2
Related
I have a trouble with next case:
template<typename T>
void test(const T &ref){
cout << "By reference";
}
template<typename T>
void test(const T *ptr){
cout << "By pointer";
}
Any parameter that I sent to the test() method will always pass to overloading with reference. Even this:
int *p = 0; test(p);
Can someone tell me why reference has so high priority and the place in standart where to read about this.
Oh... I was inattentive! I have to specify both const and non-const overloading for a pointer case:
template<typename T>
void test(const T &ref){
cout << "By reference";
}
template<typename T>
void test(T *ptr){
cout << "By pointer";
}
template<typename T>
void test(const T *ptr){
cout << "By const pointer";
}
Because const T * means that T is const but not T *.
#include <iostream>
template<typename T>
void test(const T &ref){
std::cout << "By reference\n";
}
template<typename T>
void test( T * const ptr){
std::cout << "By pointer\n";
}
int main()
{
int *p;
test(p);
return 0;
}
You can also use typedef T * PtrT, and then change T * const to const PtrT.
template <typename T>
using PtrT = T *;
template<typename T>
void test(const PtrT<T> ptr){
std::cout << "By pointer\n";
}
Can you check which type is used in template, is it int or int*? I suspect that you inspecting T to be int, but compiler interpret T as a int* and use reference template.
Try to use
test<int>(p);
to specify type explicity
I want to write a function which distinguish between arrays and pointers. This is needed in order to figure size of literal strings. I tried:
template<typename Ty>
void f(const Ty* rhs) {
std::cout << __FUNCTION__ << rhs << std::endl;
}
template<typename Ty, size_t Dm>
void f(const Ty(&rhs)[Dm]) {
std::cout << __FUNCTION__ << rhs << std::endl;
}
int main(int, char*[]) {
const char arr0[] = "test2";
const char* ptr = "test3";
const char arr6[6] = "test4";
f("test1");
f(arr0);
f(ptr);
f(arr6);
return 0;
}
But the compiler (VS2013) tells me that the call is ambiguous. Any hints?
Thanks in advance.
Unfortunately, the call are ambiguous.
As workaround, you may add an extra layer:
template<typename Ty>
void f_pointer(const Ty* rhs) {
std::cout << __FUNCTION__ << rhs << std::endl;
}
template<typename Ty, size_t Dm>
void f_array(const Ty(&rhs)[Dm]) {
std::cout << __FUNCTION__ << rhs << std::endl;
}
template<typename T>
std::enable_if_t<std::is_array<T>::value>
f(const T&t)
{
f_array(t);
}
template<typename T>
std::enable_if_t<!std::is_array<T>::value>
f(const T&t)
{
f_pointer(t);
}
Live Demo
An alternative to Jarod42's answer (which works) is to use class template specialization:
#include <iostream>
#include <type_traits>
template <typename T, bool is_array>
struct f_helper {
static void print_type (T& arg) {
std::cout << arg << " is an array\n";
}
};
template <typename T>
struct f_helper<T, false> {
static void print_type (T arg) {
std::cout << arg << " is not an array\n";
}
};
template <typename T>
void f (T& arg) {
f_helper<T, std::is_array<T>::value>::print_type (arg);
}
int main(int, char*[]) {
const char arr0[] = "test2";
const char* ptr = "test3";
const char arr6[6] = "test4";
f("test1");
f(arr0);
f(ptr);
f(arr6);
return 0;
}
Live demo
Change the reference to a pointer in the second overload of the function template:
template<typename T, size_t S> void f(T(&)[S]){
cout << "array with size " << S << "\n";
}
template <typename T> void f(T*&) {
cout << "pointer \n";
}
Live Demo
I don't believe it's possible to do what you're trying the way you're trying. The following statements evaluate to the same thing:
int* var
int var[]
It's all a matter of syntax sugar. Moreover, adding a size to the array on the function header has only the benefit of warning the user of the expected array size. Therefore the two are also equivalent (as far as the compiler is concerned):
void f(int* v)
void f(int v[8])
As one example of a broader problem, given these two overloads, you might think that the array version would take priority when an array is passed:
template <size_t N>
void bar(const char (&)[N]) {
std::cout << "array, size=" << N-1 << std::endl;
}
void bar(const char *s) {
std::cout << "raw, size=" << strlen(s) << std::endl;
}
but when passing an array (a string literal is an array), bar("hello"), the latter version (the pointer version) will be called instead.
This particular case has been discussed on SO, and the answer is interesting. But there is a general question here. I want to force the compiler to prefer one overload, and to only abandon that overload only when all legal attempts to call it have failed.
Let's rename them to bar1 and bar2 for clarity:
template <size_t N>
void bar1(const char (&)[N]) {
std::cout << "array, size=" << N-1 << std::endl;
}
void bar2(const char *s) {
std::cout << "raw, size=" << strlen(s) << std::endl;
}
Without changing those any further, can we write something like this:
template<typename ...Args>
auto try_bar1_then_bar2(Args&& ...args) -> ??? {
... will first attempt to perfect forward to bar1 ...
... only if bar1 cannot be called, fallback to bar2 ...
}
I've used some C++11 in this question, with && for perfect forwarding, but I guess the general question applies to earlier C++ also. Is there a simple, general, way to force a reordering of the overload priority? When a set of functions (with different names?) are (barely) callable, how to control exactly what order they are attempted in?
Some Expression SFINAE:
template<typename ...Args>
auto try_bar1_then_bar2_IMPL(int, Args&& ...args) -> decltype( bar1(forward<Args>(args)...) ) {
cout << "Yes, we can call bar1" << endl;
return bar1(forward<Args>(args)...);
}
template<typename ...Args>
auto try_bar1_then_bar2_IMPL(char, Args&& ...args) -> void {
cout << "No, can't call bar1, calling bar2 instead." << endl;
return bar2(forward<Args>(args)...);
}
template<typename ...Args>
auto try_bar1_then_bar2(Args&& ...args) -> decltype( try_bar1_then_bar2_IMPL(0, forward<Args>(args)...) ) {
return try_bar1_then_bar2_IMPL(0, forward<Args>(args)...);
}
When bar1 cannot be called, the first overload of try_bar1_then_bar2_IMPL is invalid because the decltype in the return type fails. But if bar1 can be called, then both are valid (and are perfectly matched, I think). I've therefore added a dummy parameter in front, an int or char, which tie breaks in favour of the call to bar1.
This is called like so:
try_bar1_then_bar2("hello"); // array, calls array version
try_bar1_then_bar2(+"hello"); // + converts to a pointer, therefore
// this calls the pointer ('raw') version.
You can wrap the arguments with a template to get the desired overloaded function:
#include <cstring>
#include <iostream>
template <typename T>
struct Wrap
{
const T& value;
Wrap(const T& value)
: value(value)
{}
};
template <typename T>
inline Wrap<T> wrap(const T& value) {
return Wrap<T>(value);
}
template <size_t N>
void bar(const Wrap<char[N]>&) {
std::cout << "array, size=" << N-1 << std::endl;
}
void bar(const Wrap<const char *>& s) {
std::cout << "raw, size=" << strlen(s.value) << std::endl;
}
template <typename T>
void bar(const T& value) {
bar(wrap(value));
}
int main(int argc, char* argv[]) {
const char a[] ="hello";
const char* s ="hello";
bar(a);
bar(s);
}
I'm relatively new to C++. Please excuse my terminology if it's incorrect. I tried searching around for an answer to my question, but I could not find it (probably because I couldn't phrase my question correctly). I'd appreciate it if someone could help me.
I am trying to write a class for creating strings that might contain the textual representation of objects or native types. Essentially, I have
private:
stringstream ss;
public:
template< typename T >
Message& operator<<( const T& value ) {
ss << value;
return *this;
}
The overloaded << operator takes some value and tries to stream it into a stringstream. I think my compiler is fine with this if the T is something like int or if the class T defines the method operator std::string(). However, if T is some type like vector<int>, then it no longer works because vector<int> doesn't define operator std::string().
Is there anyway I could perhaps overload this operator so that if T defines operator std::string(), then I print the textual representation, and if it doesn't, I just print its address?
Thanks.
This can be implemented by building upon the has_insertion_operator type trait described here: https://stackoverflow.com/a/5771273/4323
namespace has_insertion_operator_impl {
typedef char no;
typedef char yes[2];
struct any_t {
template<typename T> any_t( T const& );
};
no operator<<( std::ostream const&, any_t const& );
yes& test( std::ostream& );
no test( no );
template<typename T>
struct has_insertion_operator {
static std::ostream &s;
static T const &t;
static bool const value = sizeof( test(s << t) ) == sizeof( yes );
};
}
template<typename T>
struct has_insertion_operator :
has_insertion_operator_impl::has_insertion_operator<T> {
};
Once we have that, the rest is relatively straightforward:
class Message
{
std::ostringstream ss;
public:
template< typename T >
typename std::enable_if<has_insertion_operator<T>::value, Message&>::type
operator<<( const T& value ) {
ss << value;
return *this;
}
template< typename T >
typename std::enable_if<!has_insertion_operator<T>::value, Message&>::type
operator<<( const T& value ) {
ss << &value;
return *this;
}
};
That is, if there is an insertion operator defined, print the value, otherwise print its address.
This does not rely on a conversion-to-std::string operator being defined--you only need to make sure your T instances are "printable" using operator << (typically implemented in the same scope where each T is defined, e.g. namespace or global scope).
Here's an example - using some custom traits for a conversion operator to std::string and the streaming operator:
#include <iostream>
#include <string>
template <class T>
struct traits
{
template <typename Q>
static auto hos(Q*) -> decltype(std::declval<const Q>().operator std::string());
static char hos(...);
constexpr static bool has_operator_string =
sizeof hos((T*){0}) != 1;
// ----
template <typename Q>
static auto isab(Q*) -> decltype(std::cout << std::declval<const Q>());
static char isab(...);
constexpr static bool is_streamable =
sizeof isab((T*){0}) != 1;
};
struct S
{
template <typename T>
typename std::enable_if<
traits<T>::has_operator_string,
S&>::type
operator<<(const T& value)
{
std::cout << "string() " << value.operator std::string() << '\n';
return *this;
}
template <typename T>
typename std::enable_if<!traits<T>::has_operator_string && traits<T>::is_streamable, S&>::type
operator<<(const T& value)
{
std::cout << "<< " << value << std::endl;
return *this;
}
template <typename T>
typename std::enable_if<
!traits<T>::has_operator_string &&
!traits<T>::is_streamable,
S&>::type
operator<<(const T& value)
{
std::cout << "T& #" << &value << std::endl;
return *this;
}
};
struct X
{
operator std::string() const { return "hi"; }
};
struct Y
{
};
int main()
{
std::cout << "> main()" << std::endl;
std::cout << "X() ";
S() << X();
Y y;
std::cout << "Y y; ";
S() << y;
std::cout << "Y() ";
S() << Y();
std::cout << "\"text\" ";
S() << "text";
std::cout << "< main()" << std::endl;
}
I would like to discern between static arrays and pointers.
The following example fails to compile due to array-to-pointer conversions having exact match, making both foo's possible candidates.
Am I able to get the 2nd overload of foo to be unambiguously selected using type traits?
#include <iostream>
template<typename T>
void foo(const T* str)
{
std::cout << "ptr: " << str << std::endl;
}
template<typename T, size_t N>
void foo(const T (&str)[N])
{
std::cout << "arr: " << str << std::endl;
}
int main()
{
foo("hello world"); // I would like the array version to be selected
return 0;
}
You may use the following:
namespace detail
{
template <typename T> struct foo;
template <typename T>
struct foo<T*>
{
void operator()(const T* str) {std::cout << "ptr: " << str << std::endl;}
};
template <typename T, std::size_t N>
struct foo<T [N]>
{
void operator()(const T (&str)[N]) {std::cout << "arr: " << str << std::endl;}
};
}
template<typename T>
void foo(const T& t)
{
detail::template foo<T>()(t);
}
Live example
template<typename T>
typename std::enable_if<std::is_pointer<T>::value,void>::type
foo(const T str)
{
std::cout << "ptr: " << str << std::endl;
}
template<typename T, size_t N>
void
foo(const T (&str)[N])
{
std::cout << "arr: " << str << std::endl;
}