Firstly, I am new to C++ and Stackoverflow community. I am trying to learn singleton class. As per the answer given in the post https://stackoverflow.com/a/15310943/7023011, i tried using the same code but just defined the static object as a static data member outside the member function as shown:
struct Example
{
static Example example;
static Example& instance()
{
return example;
}
private:
Example() { }
Example(Example const&) = delete;
Example(Example&&) = delete;
Example& operator = (Example const&) = delete;
Example& operator = (Example&&) = delete;
};
But this process gives compilation error: "undefined reference to Example::example" . Can anyone suggest the difference between the two?
Related
class Foo
{
public:
Foo(int& cnt) : cnt_(cnt) {}
void test() const
{
cnt_ = 0;
}
protected:
int& cnt_;
};
int cnt;
int main()
{
Foo foo(cnt);
foo.test();
}
The above code compiles. The test function is const, however we are allowed
to change the value of cnt_. If "cnt_" is not a reference then compiler
gives an error as expected. However if "cnt_" is a reference like above,
why doesn't compiler give an error ? We are still changing the state of the
object "Foo" inside a const member function, isn't it ?
The member cnt_, declared as:
int& cnt_;
is a reference, and inside the member function:
void test() const;
the const-qualification is applied to the members, i.e.: the reference, and not to the object referenced. Therefore, the object being referenced can still be modified through that reference, even inside a const member function, like the one above.
Note that, references can't be assigned after initialization anyway, so it really doesn't change what you can do with that reference.
Perhaps a pointer analogy will help.
Let's say you have:
struct Foo
{
Foo(int* cnt) : cnt_(cnt) {}
void test1() const
{
*cnt_ = 0;
}
void test2(int* p) const
{
cnt_ = p; // Not allowed
}
int* cnt_;
};
In test1, you are not changing cnt_. You are changing the value of cnt_ points to. That is allowed.
In test2, you are changing cnt_. That is not allowed.
In your case, you are not changing cnt_ to reference another object. You are changing the value of the object cnt_ references. Hence, it is allowed.
#include <iostream>
#include <set>
using namespace std;
class StudentT {
public:
int id;
string name;
public:
StudentT(int _id, string _name) : id(_id), name(_name) {
}
int getId() {
return id;
}
string getName() {
return name;
}
};
inline bool operator< (StudentT s1, StudentT s2) {
return s1.getId() < s2.getId();
}
int main() {
set<StudentT> st;
StudentT s1(0, "Tom");
StudentT s2(1, "Tim");
st.insert(s1);
st.insert(s2);
set<StudentT> :: iterator itr;
for (itr = st.begin(); itr != st.end(); itr++) {
cout << itr->getId() << " " << itr->getName() << endl;
}
return 0;
}
In line:
cout << itr->getId() << " " << itr->getName() << endl;
It give an error that:
../main.cpp:35: error: passing 'const StudentT' as 'this' argument of 'int StudentT::getId()' discards qualifiers
../main.cpp:35: error: passing 'const StudentT' as 'this' argument of 'std::string StudentT::getName()' discards qualifiers
What's wrong with this code? Thank you!
The objects in the std::set are stored as const StudentT. So when you try to call getId() with the const object the compiler detects a problem, mainly you're calling a non-const member function on const object which is not allowed because non-const member functions make NO PROMISE not to modify the object; so the compiler is going to make a safe assumption that getId() might attempt to modify the object but at the same time, it also notices that the object is const; so any attempt to modify the const object should be an error. Hence compiler generates an error message.
The solution is simple: make the functions const as:
int getId() const {
return id;
}
string getName() const {
return name;
}
This is necessary because now you can call getId() and getName() on const objects as:
void f(const StudentT & s)
{
cout << s.getId(); //now okay, but error with your versions
cout << s.getName(); //now okay, but error with your versions
}
As a sidenote, you should implement operator< as :
inline bool operator< (const StudentT & s1, const StudentT & s2)
{
return s1.getId() < s2.getId();
}
Note parameters are now const reference.
Member functions that do not modify the class instance should be declared as const:
int getId() const {
return id;
}
string getName() const {
return name;
}
Anytime you see "discards qualifiers", it's talking about const or volatile.
Actually the C++ standard (i.e. C++ 0x draft) says (tnx to #Xeo & #Ben Voigt for pointing that out to me):
23.2.4 Associative containers
5 For set and multiset the value type
is the same as the key type. For map
and multimap it is equal to pair. Keys in an associative
container are immutable.
6 iterator of
an associative container is of the
bidirectional iterator category. For
associative containers where the value
type is the same as the key type, both
iterator and const_iterator are
constant iterators. It is unspecified
whether or not iterator and
const_iterator are the same type.
So VC++ 2008 Dinkumware implementation is faulty.
Old answer:
You got that error because in certain implementations of the std lib the set::iterator is the same as set::const_iterator.
For example libstdc++ (shipped with g++) has it (see here for the entire source code):
typedef typename _Rep_type::const_iterator iterator;
typedef typename _Rep_type::const_iterator const_iterator;
And in SGI's docs it states:
iterator Container Iterator used to iterate through a set.
const_iterator Container Const iterator used to iterate through a set. (Iterator and const_iterator are the same type.)
On the other hand VC++ 2008 Express compiles your code without complaining that you're calling non const methods on set::iterators.
Let's me give a more detail example. As to the below struct:
struct Count{
uint32_t c;
Count(uint32_t i=0):c(i){}
uint32_t getCount(){
return c;
}
uint32_t add(const Count& count){
uint32_t total = c + count.getCount();
return total;
}
};
As you see the above, the IDE(CLion), will give tips Non-const function 'getCount' is called on the const object. In the method add count is declared as const object, but the method getCount is not const method, so count.getCount() may change the members in count.
Compile error as below(core message in my compiler):
error: passing 'const xy_stl::Count' as 'this' argument discards qualifiers [-fpermissive]
To solve the above problem, you can:
change the method uint32_t getCount(){...} to uint32_t getCount() const {...}. So count.getCount() won't change the members in count.
or
change uint32_t add(const Count& count){...} to uint32_t add(Count& count){...}. So count don't care about changing members in it.
As to your problem, objects in the std::set are stored as const StudentT, but the method getId and getName are not const, so you give the above error.
You can also see this question Meaning of 'const' last in a function declaration of a class? for more detail.
Hiho,
I have implemented a singleton class in C++ which has got a problem with an initialization within its constructor where I try to initialize a member with a custom constructor but instead the compiler thinks I want to call the default constructor (without arguments) and (of course) can't find it as I don't need a default constructor for that class.
Here is the code:
class CionTokenTypes final {
private:
CionTokenTypes();
TokenType init_tt(TokenType token_type, bool skipped = false);
std::vector<TokenType> m_all_token_types;
std::vector<TokenType> m_skipped_token_types;
static const CionTokenTypes c_instance;
public:
CionTokenTypes(CionTokenTypes const&) = delete;
CionTokenTypes(CionTokenTypes &&) = delete;
static CionTokenTypes const& get_instance();
std::vector<TokenType> const& get_all() const;
std::vector<TokenType> const& get_skipped() const;
TokenType const my_custom_token_type;
};
Here is the source file:
const CionTokenTypes CionTokenTypes::c_instance = {};
TokenType CionTokenTypes::init_tt(
TokenType token_type,
bool skipped
) {
m_all_token_types.push_back(token_type);
if (skipped) {
m_skipped_token_types.push_back(token_type);
}
return std::move(token_type);
}
CionTokenTypes const& CionTokenTypes::get_instance() {
return c_instance;
}
std::vector<TokenType> const& CionTokenTypes::get_all() const {
return m_all_token_types;
}
std::vector<TokenType> const& CionTokenTypes::get_skipped() const {
return m_skipped_token_types;
}
CionTokenTypes::CionTokenTypes():
m_all_token_types{},
m_skipped_token_types{},
my_custom_token_type{init_tt({"bracket: closing bracket ]", "\\]"})}
{}
And you might also want to see the TokenType class:
class TokenType final {
public:
enum class MatchType : uint8_t {
non_greedy,
greedy
};
TokenType(
std::string const& name,
std::string const& regex = "",
TokenTypeStore store_type = TokenTypeStore::empty,
MatchType match_type = MatchType::non_greedy);
TokenType() = delete;
TokenType(TokenType const& other) = default;
TokenType(TokenType && other) = default;
TokenType & operator=(TokenType const& other) = default;
TokenType & operator=(TokenType && other) = default;
};
And its source file:
TokenType::TokenType(
std::string const& name,
std::string const& regex,
TokenTypeStore store_type,
TokenType::MatchType match_type
):
m_data{
std::make_shared<TokenType::Data>(
name,
regex,
store_type,
match_type
)
}
{}
I have cut out the not important parts as these files would have been just too huge for a complete paste at StackOverflow.
The compiler error:
/home/robbepop/coding/c++/projects/cion/src/token/cion_token_types.cpp: In constructor ‘cion::CionTokenTypes::CionTokenTypes()’:
/home/robbepop/coding/c++/projects/cion/src/token/cion_token_types.cpp:207:69: error: use of deleted function ‘cion::TokenType::TokenType()’
closing_brack {init_tt({"bracket: closing bracket ]" , "\\]"})}
^
In file included from /home/robbepop/coding/c++/projects/cion/include/token/cion_token_types.hpp:4:0,
from /home/robbepop/coding/c++/projects/cion/src/token/cion_token_types.cpp:1:
/home/robbepop/coding/c++/projects/cion/include/token/token_type.hpp:56:3: note: declared here
TokenType() = delete;
^
/home/robbepop/coding/c++/projects/cion/src/token/cion_token_types.cpp:207:69: error: use of deleted function ‘cion::TokenType::TokenType()’
closing_brack {init_tt({"bracket: closing bracket ]" , "\\]"})}
^
In file included from /home/robbepop/coding/c++/projects/cion/include/token/cion_token_types.hpp:4:0,
from /home/robbepop/coding/c++/projects/cion/src/token/cion_token_types.cpp:1:
/home/robbepop/coding/c++/projects/cion/include/token/token_type.hpp:56:3: note: declared here
TokenType() = delete;
^
src/CMakeFiles/cion_compiler.dir/build.make:1066: recipe for target 'src/CMakeFiles/cion_compiler.dir/token/cion_token_types.cpp.o' failed
make[2]: *** [src/CMakeFiles/cion_compiler.dir/token/cion_token_types.cpp.o] Error 1
CMakeFiles/Makefile2:75: recipe for target 'src/CMakeFiles/cion_compiler.dir/all' failed
make[1]: *** [src/CMakeFiles/cion_compiler.dir/all] Error 2
Makefile:76: recipe for target 'all' failed
make: *** [all] Error 2
make 62.10s user 5.93s system 98% cpu 1:08.80 total
I hope you can help me as I really don't know why the compiler thinks that I want to call the default constructor instead of my custom one ...
I finally found out what's the source of this strange compiler error:
It is in fact really strange. The source of the problem was that the header file declared about one hundred members of the CionTokenTypes class but they got initialized in the wrong order and some got even left out in the source file of the class which resulted in the error that the compiler can't find the default constructor for the last member.
So I guess this confusing was mainly caused by a poor error handling for this situation.
Could be wrong, as I do not seem to have enough of your code to properly reproduce the error in ideone.
From the looks of things your are attempting to invoke the default constructor of TokenType at line 210 and because none is being automatically generated by the compiler, you get an error. It is not being generated because you have declared another constructor and the rules of C++ then skip generation of the default constructor.
Solutions include (one of):
You weren't supposed to be calling a default constructor, so investigate line 210 and stop that.
You actually did want a default constructor, add the line TokenType() = default; to your TokenType struct.
I'm aware of the issues with the Singleton Pattern, but I wanted to use an exercise in creating a Singleton using C++11 member function specifiers to learn about C++11.
Anyway, I got this far:
#include<new>
#include<vector>
class Singleton
{
private:
Singleton() = default;
~Singleton() = default;
public:
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
void* operator new(std::size_t) = delete;
void* operator new[](std::size_t) = delete;
void operator delete(void*) = delete;
void operator delete[](void*) = delete;
static const Singleton& getInstance()
{
static Singleton mySingleton;
return mySingleton;
}
};
int main(int argc, const char *argv[])
{
const Singleton& s1 = Singleton::getInstance();
// Why does this compile?
std::vector<Singleton> v1;
// Or this?
std::vector<Singleton> v2(50);
return 0;
}
And my question is, why do these lines:
std::vector<Singleton> v1;
std::vector<Singleton> v2(50);
compile, and not report an error of a default Singleton constructor being in a private context?
I am using gcc 4.8.2 on a 64bit Linux machine, and the code compiles here as well.
Your code does not use the default constructor of Singleton outside of getInstance, since the vector is empty.
I'm building a large project on Debian 6.0.6 (with gcc 4.4.5) that was initially built in Microsoft VS (2008, I think).
What seems to be the problem is that when I declare a member as
typedef typename std::set<T>::iterator iterator, and then later use this iterator, gcc appears to interpret this as (const T*).
The part of the class containing the typename designation:
template <class entityType>
class entityArray
{
private: std::set<entityType> m_array;
public: typedef typename std::set<entityType>::iterator iterator;
...
public:
entityType* At( const char* name);
...
};
plus a few other classes that are needed for the discussion:
class entity
{
private:
entity* m_parent;
int m_ncid;
std::string m_name;
public:
entity () { m_ncid = 0; m_parent = NULL;}
virtual ~entity () {};
...
};
class attribute : public entity
{
public:
attribute(){};
virtual ~attribute(){};
};
class var : public entity
{
private:
entityArray<attribute> m_atts;
public:
var(){}
virtual ~var(){}
...
};
class dim : public entity
{
public:
dim() {};
virtual ~dim() {};
};
class group : public entity
{
private:
entityArray<var> m_vars;
entityArray<dim> m_dims;
...
public:
dim* DimAt( const char* dimname ) { return m_dims.At(dimname);}
};
Now an iterator is initialized through a call to the function DimAt which in turn calls At. The At function in the first class is defined as:
template <class entityType>
entityType* entityArray<entityType>::At( const char* name )
{
entityType dummy;
iterator iter;
entityType* ptr;
... define dummy ...
iter = m_array.find( dummy );
ptr = (iter != m_array.end()) ? &(*iter) : NULL;
return ptr;
}
Compiling the above produces
error: invalid conversion from const dim* to dim*., referring to &(*iter).
I realize that typename is required for declaring iterator, since the type is a dependent and qualified name, but I don't see why this substitution (const *) is being performed by the compiler. I would appreciate any help that you could provide. Thanks!
This has absolutely nothing to do with typename.
The standard allows std::set<T>::iterator and std::set<T>::const_iterator to be the same type, and with GCC the types are the same.
The reason is that modifying an element of a std::set e.g. by *iter = val might invalidate the ordering of the set elements, breaking the invariant that the elements of the set are always in order. By making the iterator type a constant iterator instead of a mutable iterator it's not possible to alter the element, preventing you from corrupting the set's ordering.
So with GCC's implementation, when you dereference the iterator using *iter you get a const entitType& and when you take its address using &*iter you get a const entityType*