Is it possible to establish a set of templated function pointers, without the hassle of doing so manually? Here's an example to illustrate what the heck I'm talking about.
Let's say I have a frequently-called function "write" of which I have two implementations (write0 and write1) that I'd like to be able to switch between dynamically. These write functions are templated on the argument type. One way to do this is to just have a templated front-end function write() which internally uses an if statement.
This turns out to be fast enough for my needs, but now I was left wondering if I can do the same using function pointers (just for fun). The problem with this approach is that setting up the function pointers is a hassle. Are there any other ways to essentially achieve the ideal of write() but without the conditional (direct static dispatch)?
(Other "rules": I can't change the Msg classes to have write() methods, and I can't change the use site code to replace Msgs with adaptors for Msgs.)
FWIW, I found this article basically saying the same thing I'm saying here.
#include <iostream>
using namespace std;
template<typename T> void write0(T msg) { cout << "write0: " << msg.name() << endl; }
template<typename T> void write1(T msg) { cout << "write1: " << msg.name() << endl; }
// This isn't so bad, since it's just a conditional (which the processor will
// likely predict correctly most of the time).
bool use_write0;
template<typename T> void write(T msg) { if (use_write0) write0(msg); else write1(msg); }
struct MsgA { const char *name() { return "MsgA"; } };
struct MsgB { const char *name() { return "MsgB"; } };
struct MsgC { const char *name() { return "MsgC"; } };
struct MsgD { const char *name() { return "MsgD"; } };
// This doesn't work: templates may not be virtual.
#if 0
struct Writer { template<typename T> virtual void write(T msg) = 0; };
struct Writer0 { template<typename T> virtual void write(T msg) { cout << "write0: " << msg.name() << endl; } };
struct Writer1 { template<typename T> virtual void write(T msg) { cout << "write0: " << msg.name() << endl; } };
#endif
int main(int argc, char **argv) {
use_write0 = argc == 1;
// I can do this:
write(MsgA());
// Can I achieve the following without the verbosity (manual setup, named
// template instantiations, etc.)?
void (*pwriteA)(MsgA) = use_write0 ? (void(*)(MsgA)) write0<MsgA> : (void(*)(MsgA)) write1<MsgA>;
void (*pwriteB)(MsgB) = use_write0 ? (void(*)(MsgB)) write0<MsgB> : (void(*)(MsgB)) write1<MsgB>;
void (*pwriteC)(MsgC) = use_write0 ? (void(*)(MsgC)) write0<MsgC> : (void(*)(MsgC)) write1<MsgC>;
void (*pwriteD)(MsgD) = use_write0 ? (void(*)(MsgD)) write0<MsgD> : (void(*)(MsgD)) write1<MsgD>;
pwriteA(MsgA());
pwriteB(MsgB());
pwriteC(MsgC());
pwriteD(MsgD());
return 0;
}
If you want to switch logging functions back and forth while the program runs, I think you have to manually set the function pointer for each type.
If it's enough to just choose the logging function at startup, it can be done in a fully generic way without even knowing for which types the function will be called later:
// writer functions
template<typename T> void write0(T msg) { std::cout << 0; };
template<typename T> void write1(T msg) { std::cout << 1; };
// global flag
bool use_write0;
// function pointers for all types
template<typename T>
struct dispatch {
typedef void (*write_t)(T);
static write_t ptr;
};
// main write function
template<typename T>
inline void write(T msg) {
(*dispatch<T>::ptr)(msg);
}
// the fun part
template<typename T>
void autoinit(T msg) {
if (use_write0)
dispatch<T>::ptr = &write0<T>;
else
dispatch<T>::ptr = &write1<T>;
// call again for dispatch to correct function
write(msg);
}
// initialization
template<typename T>
typename dispatch<T>::write_t dispatch<T>::ptr = &autoinit<T>;
// usage example
int main(int argc, char **argv) {
use_write0 = (argc == 1);
write("abc");
return 0;
}
For each type T the first call to write<T>() decides which writing function should be used. Later calls then directly use the function pointer to that function.
You could also use Don Clugston's FastDelegates header. Generates no runtime overhead whatsoever and truly object-oriented delegates. While the syntax for using them is not perfect, it is a bit simpler than fiddling with raw function pointers.
Why don't you use an array of function pointers?
#include <iostream>
using namespace std;
template<typename T> void write0(T msg) { cout << "write0: " << msg.name() << endl; }
template<typename T> void write1(T msg) { cout << "write1: " << msg.name() << endl; }
template<typename T> struct WriteSelector
{
static void(* const s_functions[])(T msg);
};
template<typename T> void(* const WriteSelector<T>::s_functions[])(T msg)=
{
&write0<T>,
&write1<T>
};
unsigned write_index=0;
template<typename T> void write(T msg)
{
WriteSelector<T>::s_functions[write_index](msg);
}
struct MsgA { const char *name() { return "MsgA"; } };
struct MsgB { const char *name() { return "MsgB"; } };
struct MsgC { const char *name() { return "MsgC"; } };
struct MsgD { const char *name() { return "MsgD"; } };
void Test()
{
write(MsgA());
write(MsgB());
write(MsgC());
write(MsgD());
}
int main()
{
Test();
write_index=1;
Test();
return 0;
}
There are two axises of variation in writing: the write0/write1 choice and the MsgA/B/C.... choice.
Conceptually that means you need NxM implementations of a write function. Of course, if a write implementation is added, or a message type is added, this leads to resp. M or N extra functions to be added.
For both axises you can choose whether to implement them using static or dynamic polymorphism. The static polymorphism can be done using templates or using function overrides.
It could be done by creating a N element class hierarchy with M write functions in each class. But it would soon become a maintenance nightmare. Unless the message content is also runtime polymorphic. But the question is about static polymorphism for the messages.
Since runtime polymorphism is ruled out because of too elaborate (and you can't have a template function virtual, which would decrease the verbosity of overrides), we need to implement a little type-dispatching routine, converting runtime information into compile-time information.
More specifically: templatize the main action (in the example called Tmain) with the writer-to-use, and call it with the right template argument from the 'real' main.
This omits the use of a 'global' choice variable, yet is object-oriented and concise.
// twodimensionalpolymorph.cpp
//
#include <iostream>
using namespace std;
class Write0 {
public:
template< typename tMsg >
void operator()( /*const*/ tMsg& msg ) { cout << "write0: " << msg.name() << endl; };
};
class Write1 {
public:
template< typename tMsg >
void operator()( /*const*/ tMsg& msg ) { cout << "write1: "<< msg.name() << endl; };
};
struct MsgA { const char *name() { return "MsgA"; } };
struct MsgB { const char *name() { return "MsgB"; } };
struct MsgC { const char *name() { return "MsgC"; } };
struct MsgD { const char *name() { return "MsgD"; } };
// the Tmain does the real action
//
template< typename Writer >
int Tmain( Writer& write, int argc, char** args ) {
write( MsgA() );
write( MsgB() );
write( MsgB() );
write( MsgD() );
return 0;
}
// the main merely chooses the writer to use
//
int main( int argc, char** args ) {
if( argc==1 )
return Tmain( Write0(), argc, args);
else
return Tmain( Write1(), argc, args);
}
Related
I have a template member function declared in a class that call the correct member function depending on type, and want to add some functionality to it in a daughter class, by adding a member function, like in the main.cpp example below :
#include <iostream>
class A
{
public:
template <typename T>
void handleSocketData(const T& t)
{
handleData(t);
}
void handleData(int data)
{
std::cout << data << std::endl;
}
};
class B: public A
{
public :
void handleData(std::string data) const
{
std::cout << data << std::endl;
}
};
int main(int argc, char *argv[])
{
A a;
B b;
a.handleSocketData<int>(30);
b.handleSocketData<std::string>("Hi");
return 0;
}
My problem is that b.handleSocketData<QString>("Hi"); actually does generate a new template instance in A class as shown in the output of command /usr/bin/clang++ -DQT_CORE_LIB -isystem /usr/include/qt6/QtCore -isystem /usr/include/qt6 -isystem /usr/lib64/qt6/mkspecs/linux-g++ -g -std=gnu++17 -Xclang -ast-print -fsyntax-only main.cpp:
class A {
public:
template <typename T> void handleSocketData(const T &t) {
this->handleData(t);
}
template<> void handleSocketData<int>(const int &t) {
this->handleData(t);
}
template<> void handleSocketData<std::basic_string<char>>(const std::basic_string<char> &t) {
<recovery-expr>(this->handleData, t);
}
void handleData(int data) {
std::cout << data << std::endl;
}
};
class B : public A {
public:
void handleData(std::string data) const {
std::cout << data << std::endl;
}
};
int main(int argc, char *argv[]) {
A a;
B b;
a.handleSocketData<int>(30);
b.handleSocketData<std::string>("Hi");
return 0;
}
So right now I have a compilation error, saying that no function handleData(const std::string& data) is found, which is normal.
A workaround we've found is to define a two-arguments template, taking the daughter class as argument (kind of visitor pattern) :
#include <iostream>
class A
{
public:
template <typename T, typename U>
void handleSocketData(U& u, const T& t)
{
u.handleData(t);
}
void handleData(int data)
{
std::cout << data << std::endl;
}
};
class B: public A
{
public :
void handleData(std::string data)
{
std::cout << data << std::endl;
}
};
int main(int argc, char *argv[])
{
A a;
B b;
a.handleSocketData<int>(a, 30);
b.handleSocketData<std::string>(b, "Hi");
return 0;
}
What do you think ? Is there a cleaner way ?
This looks like a classic use case for CRTP. You can make A a template over a derived class Derived and then dispatch function calls to the derived class via a static_cast. For this to work, any derived class Derived must be derived from A<Derived>.
Since you seem to want to use A as a non-abstract class, you would have to add a default derived class marking it as "final". In the following code, the empty struct FinalTag serves this purpose.
#include <iostream>
struct FinalTag;
template <typename Derived=FinalTag>
class A
{
public:
template <typename T>
void handleSocketData(const T& t)
{
cast().handleData(t);
}
void handleData(int data)
{
std::cout << data << std::endl;
}
private:
constexpr auto& cast() {
return static_cast<Derived&>(*this);
}
};
struct FinalTag : A<FinalTag> {};
class B: public A<B>
{
public :
using Base = A<B>;
using Base::handleData;
void handleData(std::string data)
{
std::cout << data << std::endl;
}
};
int main(int argc, char *argv[])
{
A a;
B b;
a.handleSocketData(30);
b.handleSocketData("Hi");
// this only works if you bring in Base::handleData in the
// derived class
b.handleSocketData(30);
return 0;
}
Live Code: https://godbolt.org/z/ns9aPjG76
This is a prototype. You would want to add a const version to the cast method for instance.
Edit:
As Jarod42 pointed out in the comments, C++23 really simplifies CRTP with "deducing this": https://godbolt.org/z/cGzMrnEhc. This isn't currently widely supported by compilers though.
A slightly different version of CRTP to the one suggested by Joerg Brech could be more suitable in some cases.
#include <iostream>
class A
{
public:
template <class Class, typename T>
void handleSocketData(const T& t)
{
static_cast<Class*>(this)->handleData(t);
}
void handleData(int data)
{
std::cout << data << std::endl;
}
};
class B: public A
{
public :
void handleData(std::string data) const
{
std::cout << data << std::endl;
}
};
int main(int argc, char *argv[])
{
A a;
B b;
a.handleSocketData<A, int>(30);
b.handleSocketData<B, std::string>("Hi");
return 0;
}
It is very similar to your solution in the sense that we instruct handleSocketData which class it should use to call handleData from. The only difference is that the decision is made not dynamically but at compile time.
I have the following class:
class Karen
{
public:
Karen(void);
~Karen(void);
void complain(std::string level);
private:
void debug(void) const;
void info(void) const;
void warning(void) const;
void error(void) const;
};
The complain function receives a string that can contain the words debug, info, warning or error, and it has to call the appropriate function without using a forest of if/elseif/else, using instead pointers to member functions. The prototype of complain is given to me. I am new to pointers to member functions and I am not sure how to manage this. One of my attempts is this:
void Karen::complain(std::string level)
{
std::string *p = &level;
void (Karen::*f)(void) const;
(this->*(*p))();
}
The syntax of the last line is incorrect, but I am trying to do (this->*(content of pointer p))() and I don't know how to write this. Can someone help me?
Edit I am only allowed to use C++98
Syntax to call a member function via member function pointer is
(this->*memf)();
You cannot magically turn the string into a member function pointer. Sloppy speaking, names of functions do not exist at runtime. If you want such mapping you need to provide it yourself. No way around that. What you can avoid is the "forest of if-else" by using a std::unordered_map:
#include <unordered_map>
#include <string>
#include <iostream>
class Karen
{
public:
void complain(std::string level) {
static const std::unordered_map<std::string, void(Karen::*)() const> m{
{"debug",&Karen::debug},
{"info",&Karen::info},
{"warning",&Karen::warning},
{"error",&Karen::error}
};
auto it = m.find(level);
if (it == m.end()) return;
(this->*(it->second))();
}
private:
void debug(void) const { std::cout << "debug\n"; }
void info(void) const { std::cout << "info\n"; }
void warning(void) const { std::cout << "warning\n"; }
void error(void) const { std::cout << "error\n"; }
};
int main() {
Karen k;
k.complain("info");
}
Live Demo
As mentioned in comments, you could use an enum in place of the string. When possible you should use the help of the compiler, which can diagnose a typo in an enum but not in a string. Alternatively you could directly pass a member function pointer to complain. Then implementation of complain would be trivial, no branching needed. Though this would require the methods to be public and the caller would have to deal with member function pointers.
If you are not allowed to use C++11 or newer you should have a serious talk with your teacher. Soon C++20 will be the de facto standard and things have changed quite a lot. I am not fluent in C++98 anymore, so here is just a quick fix of the above to get it working somehow. You cannot use std::unordered_map but there is std::map and initialization of the map is rather cumbersome:
#include <map>
#include <string>
#include <iostream>
class Karen
{
typedef void(Karen::*memf_t)() const;
typedef std::map<std::string,void(Karen::*)() const> map_t;
public:
void complain(std::string level) {
map_t::const_iterator it = get_map().find(level);
if (it == get_map().end()) return;
(this->*(it->second))();
}
private:
const map_t& get_map(){
static const map_t m = construct_map();
return m;
}
const map_t construct_map() {
map_t m;
m["debug"] = &Karen::debug;
m["info"] = &Karen::info;
m["warning"] = &Karen::warning;
m["error"] = &Karen::error;
return m;
}
void debug(void) const { std::cout << "debug\n"; }
void info(void) const { std::cout << "info\n"; }
void warning(void) const { std::cout << "warning\n"; }
void error(void) const { std::cout << "error\n"; }
};
int main() {
Karen k;
k.complain("info");
}
Live Demo
Let us start with the required includes.
#include <cassert>
#include <iostream>
#include <map>
Providing the log level as a string, can lead to errors, since the compiler cannot check for typos. Hence an enum::class would be a better choice for determining the log level.
enum class LogLevel {
DEBUG,
INFO,
WARNING,
ERROR
};
C++ does not offer a way to obtain a function pointer given a string. After compiling and linking the function names all have been replaced by their appropriate addresses in memory. Hence, we first need to store the function pointers, in a way that allows us to look them up as needed. For this purpose you can use a static class attribute, and store the function pointers in a std::map.
class Logger
{
public:
Logger();
~Logger();
void complain(LogLevel level);
private:
void debug() const;
void info() const;
void warning() const;
void error() const;
using HandlerMap = std::map<LogLevel, void (Logger::*)(void) const>;
static HandlerMap handlers;
};
Logger::HandlerMap Logger::handlers{
{LogLevel::DEBUG, &Logger::debug},
{LogLevel::INFO, &Logger::info},
{LogLevel::WARNING, &Logger::warning},
{LogLevel::ERROR, &Logger::error}
};
The complain method then just needs to look up the correct function pointer and call the method.
void Logger::complain(LogLevel level) {
assert(handlers.find(level) != handlers.end());
(this->*handlers[level])();
}
The remaining functions look as follows.
Logger::Logger() {}
Logger::~Logger() {}
void Logger::debug() const { std::cout << "debug" << std::endl; }
void Logger::info() const { std::cout << "info" << std::endl; }
void Logger::warning() const { std::cout << "warning" << std::endl; }
void Logger::error() const { std::cout << "error" << std::endl; }
int main(int argc, char* argv[]) {
Logger k;
k.complain(LogLevel::DEBUG);
k.complain(LogLevel::INFO);
k.complain(LogLevel::WARNING);
k.complain(LogLevel::ERROR);
}
Note, if you insist on using strings, you can replace LogLevel by std::string and LogLevel::<member> by the corresponding string.
The same can be achived using C++98. However, you will need a bit more bootstrapping.
#include <cassert>
#include <iostream>
#include <map>
enum LogLevel {
LL_DEBUG,
LL_INFO,
LL_WARNING,
LL_ERROR
};
class Logger
{
public:
Logger();
~Logger();
void complain(LogLevel level);
typedef std::map<LogLevel, void (Logger::*)() const> HandlerMap;
friend struct LoggerInit;
private:
void debug() const;
void info() const;
void warning() const;
void error() const;
static HandlerMap handlers;
};
Logger::HandlerMap Logger::handlers = Logger::HandlerMap();
struct LoggerInit {
LoggerInit() {
Logger::handlers[LL_DEBUG] = &Logger::debug;
Logger::handlers[LL_INFO] = &Logger::info;
Logger::handlers[LL_WARNING] = &Logger::warning;
Logger::handlers[LL_ERROR] = &Logger::error;
}
} logger_init;
void Logger::complain(LogLevel level) {
assert(handlers.find(level) != handlers.end());
(this->*handlers[level])();
}
Logger::Logger() {}
Logger::~Logger() {}
void Logger::debug() const { std::cout << "debug" << std::endl; }
void Logger::info() const { std::cout << "info" << std::endl; }
void Logger::warning() const { std::cout << "warning" << std::endl; }
void Logger::error() const { std::cout << "error" << std::endl; }
int main(int argc, char* argv[]) {
Logger k;
k.complain(LL_DEBUG);
k.complain(LL_INFO);
k.complain(LL_WARNING);
k.complain(LL_ERROR);
}
I got to know that we can also pass template arguments to choose which function should execute. I found them good alternative to function pointers since function pointers has run time cost but template parameters does not. Also, template parameters can be made inline whereas function pointers are not.
Alright then, this is what I wrote to depict my understanding on it. I came close but missing some minor detail somewhere.
template<class T>
class String {
public:
T str;
String() { std::cout << "Ctor called" << std::endl; }
};
template<class T, class C>
int compare(const String<T> &str1,
const String<T> &str2) {
for (int i = 0; (i < str1.length()) && (i < str2.length()); ++i) {
if (C::eq(str1[i], str2[i])) {
return false;
}
}
return true;
}
template<class T>
class Cmp1 {
static int eq(T a, T b) { std::cout << "Cmp1 called" << std::endl; return a==b; }
};
template<class T>
class Cmp2 {
static int eq(T a, T b) { std::cout << "Cmp2 called" << std::endl; return a!=b; }
};
int main() {
String<std::string> s;
s.str = "Foo";
String<std::string> t;
t.str = "Foo";
compare<String<std::string>, Cmp1<std::string> >(s, t);
// compare(s, t);
}
Details of the code:
I have an class String, which take an parameter and create member function of that type.
I have an compare function, which takes two String& arguments. Comparison function is passed to it.
Cmp1 and Cmp2 are two compare functions.
compare<String<std::string>, Cmp1<std::string> >(s, t);
does not get compile here. I tried some other ways to call but in vain.
Looks like you want something like that:
#include <iostream>
#include <string>
template<class T>
class String {
public:
T str;
String() { std::cout << "Ctor called" << std::endl; }
};
template<class T, class C>
int compare(const String<T> &str1,
const String<T> &str2) {
for (int i = 0; (i < str1.str.length()) && (i < str2.str.length()); ++i) {
if (C::eq(str1.str[i], str2.str[i])) {
return false;
}
}
return true;
}
template<class T>
class Cmp1 {
public:
static int eq(T a, T b) { std::cout << "Cmp1 called" << std::endl; return a==b; }
};
template<class T>
class Cmp2 {
public:
static int eq(T a, T b) { std::cout << "Cmp2 called" << std::endl; return a!=b; }
};
int main() {
String<std::string> s;
s.str = "Foo";
String<std::string> t;
t.str = "Foo";
compare<std::string, Cmp1<char> >(s, t);
// compare(s, t);
}
code
Explanations:
You already have String in definition of compare, you need to just send T which is std::string in your case.
You are trying to go through entire std::string, in compare, so, now your code compiles.
You calling cmp on str[index], that is actually char, so you need to call cmp with char template argument.
I have the following code with a custom Variant class and a custom SmartPtr class:
using namespace std;
class Object
{
public:
};
template<typename T>
class SmartPtr
{
public:
template<typename Y>
explicit SmartPtr(Y* p) { p_ = p; }
SmartPtr(std::nullptr_t) { p_ = nullptr; }
private:
T* p_;
};
class Variant
{
public:
Variant(bool b) : _b(b) { }
private:
bool _b;
};
class Obj
{
public:
void test(SmartPtr<Object> /*p*/) { cout << "smartptr version!" << endl; }
void test(Variant /*v*/) { cout << "variant version!" << endl; }
};
int main(int argc, const char *argv[])
{
Obj o;
o.test(nullptr); // calls SmartPtr version
o.test(true); // calls Variant version
o.test(false); // -> compiler error: ambiguous call to overloaded function
return 0;
}
I assume that the boolean false can be converted both to the Variant and to 0 then to nullptr and then to SmartPtr, which causes this error.
Any chances of avoiding this conversion?
For the user of the library an API which works with 'o.test(true);' but requires something like 'o.test(Variant(false));' to compile is not very intuitive.
I believe I have an ideal solution. It only requires that the test function be altered, so it leaves SmartPtr and Variant alone, which is ideal. It adds a non-defined templated overload to test that has specializations for bool and nullptr that are defined. This directly dispatches bool and nullptr to the desired specialization, but causes link errors on other unhandled types. I'm so glad to have this worked out because I've certainly run into this in many forms myself. I wish you could use explicit of function parameters!!
I got the idea from here: C++ templates that accept only certain types
using namespace std;
class Object
{
public:
};
class Variant
{
public:
Variant( bool b) : _b(b) { }
private:
bool _b;
};
template<typename T>
class SmartPtr
{
public:
SmartPtr(std::nullptr_t null) { p_ = nullptr; }
template<typename Y>
SmartPtr(Y* p) { p_ = p; }
private:
T* p_;
};
class Obj
{
public:
void test(SmartPtr<Object> here /*p*/) {
cout << "smartptr version!" << endl;
}
void test(Variant /*v*/) { cout << "variant version!" << endl; }
template<typename T> void test(T t);
template<>
void test<bool>(bool b) {
cout << "bool specialization" << endl;
test(Variant(b));
}
template<>
void test<std::nullptr_t>(std::nullptr_t null) {
cout << "nullptr specialization" << endl;
test(SmartPtr<Object>(nullptr));
}
};
int main(int argc, const char *argv[])
{
Obj o;
Obj c;
Object object;
//o.test(3); // Gives link error LNK2019
o.test(Variant(true)); // calls Variant version
o.test(SmartPtr<Object>(&object)); // calls SmartPtr version
o.test(nullptr); // dispatched to SmartPtr version by nullptr specialization
o.test(true); // dispatched to Variant version by bool specialization
o.test(false); // dispatched to Variant version by bool specialization
return 0;
}
I had already answered with something not ideal, so I leave that answer in tact as what follows:
=============================================
I don't have an ideal solution here, and I don't know the constraints you have on your code so this may not be of functional use to you, but the following is sensible. It disallows code to use nullptr at compile time and relies on a global null_smart constant to be used in all cases where the caller is simply showing no interest in passing an object.
#include <iostream>
using namespace std;
class Object
{
public:
};
class Variant
{
public:
Variant(bool b) : _b(b) { }
private:
Variant(std::nullptr_t) {};
private:
bool _b;
};
template<typename T>
class SmartPtr
{
public:
SmartPtr() { p_ = nullptr; }
template<typename Y>
SmartPtr(Y* p) { p_ = p; }
private:
T* p_;
};
class Obj
{
public:
void test(SmartPtr<Object> /*p*/) { cout << "smartptr version!" << endl; }
void test(Variant /*v*/) { cout << "variant version!" << endl; }
};
const SmartPtr<Object> null_smart;
int main(int argc, const char *argv[])
{
Obj o;
o.test(null_smart); // calls SmartPtr version, without interest in passing object
o.test(true); // calls Variant version
o.test(false); // calls Variant version
return 0;
}
It's cleaner than the true/Variant(false) issue, but still a bit on the picky side.
I would like to write a member function which detects if the instantiated object is const.
To give a simple example, we can consider the following class definition
class Foo{
public:
void constnessChecker(){
bool isConst;
// MORE CODE GOES HERE...
if (isConst) {
std::cout << "This instance is const! << std::endl;
} else {
std::cout << "This instance is not const! << std::endl;
}
}
};
and the following code
int main(){
Foo foo1;
Foo const foo2;
foo1.constnessChecker();
foo2.constnessChecker();
}
which should produce
This instance is not const!
This instance is const!
Is this possible?
Provide const and non-const overloads:
class Foo
{
public:
void constnessChecker(){
std::cout << "This instance is not const\n";
}
void constnessChecker() const {
std::cout << "This instance is const\n";
}
....
};
In the style of boost::is_const or std::is_const, you can also write up the following:
#include <iostream>
template <typename T>
struct is_const
{
static const bool value = false;
};
template <typename T>
struct is_const<const T*>
{
static const bool value = true;
};
struct S
{
void f() const
{
std::cout << is_const<decltype(this)>::value << std::endl;
}
void f()
{
std::cout << is_const<decltype(this)>::value << std::endl;
}
int m;
};
int main(int argc, char** argv)
{
const S& cs = S(); // note that choosing a const-ref is merely to force the compiler to choos S::f() const!
cs.f (); // prints 1
S().f (); // prints 0
return 0;
}
I haven't looked at the implementation of std::is_const but for some reason it returns false where the above is_const returns true.
Note: Obviously you need support for decltype and thus the above will only work for C++11 compliant compilers.