This is a question on the syntax of C++ initializer lists.
Is it possible to call functions from initializer lists without them being arguments to member object constructors?
Code example listed below is paraphrased (paracoded?) from a similar situation at work.
The Situation
A member variable takes a pointer to a singleton as constructor
argument.
The member variable is constructed by initializer list in its containing class' constructor.
The singleton has not been created prior to the containing class being constructed.
The Code
#include <iostream>
#define LOG { std::cout << __PRETTY_FUNCTION__ << std::endl; }
namespace
{
template <class T>
class SingletonService
{
public:
static T* Instance() { LOG; return mpT; }
static void InstallInstance(T* pT) { LOG; mpT = pT; }
static void DeleteInstance() { if (mpT) delete mpT; }
protected:
static T* mpT;
};
template <class T>
T* SingletonService<T>::mpT = NULL;
class OneOfMe
{
public:
OneOfMe() { LOG; };
virtual ~OneOfMe() { };
};
class Container
{
public:
Container(OneOfMe* pObj) { LOG; /* Do something with pObj */ }
virtual ~Container() { }
};
int GenerateNum()
{
return 42;
}
class Baz
{
public:
Baz(int num) : mNum(num) { LOG; }
virtual ~Baz() { }
protected:
int mNum;
};
class Bar
{
public:
Bar() : mBaz(GenerateNum()) { LOG; } // Perfectly OK to call function that is argument to member object's non-default ctor.
virtual ~Bar() { };
protected:
Baz mBaz;
};
class Foo
{
public:
Foo()
: SingletonService<OneOfMe>::InstallInstance(new OneOfMe) // Compile error
, mContainer(SingletonService<OneOfMe>::Instance()) { }
virtual ~Foo() { };
protected:
Container mContainer;
};
}
int main(int argc, char* argv[])
{
LOG;
Bar bar;
SingletonService<OneOfMe>::InstallInstance(new OneOfMe); // This works.
Container container(SingletonService<OneOfMe>::Instance()); // And this works.
SingletonService<OneOfMe>::DeleteInstance();
return 0;
}
The compile error
>g++ main.cpp
main.cpp: In constructor ‘<unnamed>::Foo::Foo()’:
main.cpp:45: error: expected class-name before ‘(’ token
main.cpp:45: error: no matching function for call to
‘<unnamed>::Container::Container()’
main.cpp:37: note: candidates are:
<unnamed>::Container::Container(<unnamed>::OneOfMe*)
main.cpp:35: note:
<unnamed>::Container::Container(const<unnamed>::Container&)
main.cpp:45: error: expected ‘{’ before ‘(’ token
The Question
Is it syntactically possible to call a function from a class constructor's initializer list without being an argument to a member object's non-default constructor?
The question is for academic curiosity. I know at least one other solutions is to instantiate the singleton before creating the containing class.
You can utilize the comma operator.
In your example
class Foo
{
public:
Foo()
: mContainer((SingletonService<OneOfMe>::InstallInstance(new OneOfMe),
SingletonService<OneOfMe>::Instance()))
{}
virtual ~Foo();
protected:
Container mContainer;
};
Note the additional parentheses around the two expressions, otherwise these would be interpreted as two instead of one parameter.
Another approach to this particular problem could be to return the singleton from InstallInstance() as well, e.g.
template <class T>
class SingletonService {
public:
static T *InstallInstance(T *pT) { LOG; return mpT = pT; }
};
and then
class Foo {
public:
Foo()
: mContainer(SingletonService<OneOfMe>::InstallInstance(new OneOfMe)) {}
virtual ~Foo();
protected:
Container mContainer;
};
Is it possible to call functions from initializer lists without them being arguments to member object constructors?
Something like this maybe works as intended:
void f() {}
struct S {
S(): i{(f(), 0)} {}
int i;
};
int main() {
S s;
}
The basic idea is to rely on the comma operator. In this case, the value returned by the function (if any) is discarded and not used to initialize a member.
Of course, we still exploit the fact that a data member exists, so maybe it is not exactly what you are looking for.
If you want to get rid completely of data members, you can do something similar with a delegated constructor like in the following example:
void f() {}
class S {
S(int) {}
public:
S(): S{(f(), 0)} {}
};
int main() {
S s{};
}
No matters what's the return type of the invoked function. By means of the comma operator, the int value is used as a tag to dispatch the call to the right compiler and then it is discarded.
Related
I am developing the following class, that inherits a template class.
template<typename T>
class test : public T
{
public:
void init() {
T::init();
abc = true;
}
private:
bool abc;
}
On one of my base class I have the following singleton method:
class foo : protected bar
{
public:
static foo &getInstance();
void init();
private:
foo();
foo(foo const&);
void operator=(foo const&);
~foo() {};
}
When I create the following instance:
test<foo> &instance = test<foo>::getInstance();
I get an error:
invalid initialization of reference of type test<foo>& from expression of type foo
Do you know what is happening?
Thanks
test<foo>::getInstance() resolves to foo::getInstance(). That function returns a foo&, not a test<foo>&. A foo& cannot be converted to test<foo>&. Hence the compiler error.
Use
foo& instance = test<foo>::getInstance();
If you must have a test<foo>&, you need to implement getInstance() in test.
template<typename T>
class test : public T
{
public:
void init() {
T::init();
abc = true;
}
static test& getInstance()
{
static test instance;
return instance;
}
private:
bool abc;
};
But then, you'll have to take care of the cascading effects. You'll have to make sure that the constructors and destructors of foo are declared as either public or protected. They cannot be private.
I have a lambda that I need to convert into a callable object so that I can specialize the call operator. My impression has always been that a lambda with an void(auto) signature was equivalent to a callable struct roughly like this:
struct callable {
Foo & capture;
template< typename T >
void operator()( T arg ) { /* ... */ }
}
However, a lambda can access private and protected members when it is declared within a member function.
Here's a simplified example:
#include <iostream>
using namespace std;
class A {
protected:
void a() { cout << "YES" << endl; }
};
class B : public A {
public:
void call1();
void call2();
};
struct callable {
B * mB;
void operator()() {
// This does not compile: 'void A::a()' is protected within this context
// mB->a();
}
};
void B::call1() {
// but then how does this access a() ?!
[&]() { a(); }();
}
void B::call2() {
callable c{ this };
c();
}
int main()
{
B b;
b.call1();
b.call2();
}
Is there any way to emulate that behavior in a callable struct, without declaring it in the header and making it a friend class? That seems problematic because I'm going to have a lot of different callables. I'm also just curious about it, because I was under the impression that lambdas were functionally identical to declaring a struct with a call operator.
Access rights of a lambda capturing this seems to say that the lambda has the same access as a local class. But in my case, I need to emulate a generic lambda, and local classes can't have template member functions.
You can still capture this and &B::a
struct callable {
B* mB;
void (A::*m)();
void operator()() const {
(mB->*m)();
}
};
void B::call2() {
callable c{ this, &B::a };
c();
}
Demo
You can make the struct callable a friend of B without providing a definition for it. Such a declaration doesn't even make the name visible until the real declaration is encountered:
class B : public A {
// ...
friend struct callable;
}
// ERROR: callable isn't visible here:
// callable *foo;
This can be extended to hack in a large number of friends with one declaration if brevity is really important:
class B : public A {
// ...
template<int> friend struct callable;
};
// In implementation:
template<>
struct callable<0> { /* ... */ };
template<>
struct callable<1> { /* ... */ };
I'm an C++ beginner, please help me.
I can't use template class as the constructor' s parameter.
xcode shows 'No matching constructor for initialization of 'Work'' error.
The whole source code below, any one can fix this?
#include <iostream>
class Base {
public:
virtual void hello_world() const {
printf("Base::hello_world()\n");
};
};
class Derived : public Base {
public:
void hello_world() const {
printf("Derived::hello_world()\n");
};
};
template<class T>
class Templ {
public:
Templ(const T &t) : _value(t) {}
const T& getValue() const{
return _value;
}
private:
const T &_value;
};
class Work {
public:
Work(const Templ<Base*> &base) : mBase(base) {}
void working() {
mBase.getValue()->hello_world();
}
private:
const Templ<Base*> &mBase;
};
int main(int argc, const char * argv[]) {
Templ<Base*> base(new Base());
//OK
Work w_base(base);
Templ<Derived*> derived(new Derived());
//error: No matching constructor for initialization of 'Work'
Work w_derived(derived);
return 0;
}
Work w_derived(derived); is never going to work as Work expects a Templ<Base*>. A Templ<Base*> and a Templ<Derived*> are two different, distinct types. Just a like a std::vector<int> is not the same as a std::vector<std::complex>.
What you can do though is create a Templ<Base*> from a pointer to a Dervied and then create a Work with that. Something like
Templ<Base*> derived(new Derived());
Work w_derived(derived);
Live Example
Also as pointed out in the comments since you are using polymorphism you need to have a virtual destructor in the base class. If the destructor is not virtual then only the base class destructor will run and you will your object will not be properly destructed.
In C++, this would look like this
struct Base
{
virtual ~Base() {} // enable descruction of base through pointer to Base
virtual void hello_world() const
{ std::cout<<"Base::hello_world()\n"; }
};
struct Derived : Base
{
void hello_world() const override
{ std::cout<<"Derived::hello_world()\n"; }
};
struct work
{
work(const Base*p)
: ptr(p) {}
void working() const
{ ptr->hello_world(); }
private:
std::unique_ptr<const Base> ptr;
};
int main()
{
Work w_base(new Base);
Work w_derived(new Derived);
w_base.working();
w_derived.working();
}
Note the following
the virtual destructor of Base ensures that a derived class is properly destructed from a pointer to Base, so that std::unique_ptr<> works correctly.
the override keyword ensures that we actually implement a virtual method.
the usage of std::unique_ptr<> avoids the Templ class. Morever, its destructor will automatically and correctly destroy the pointed-to object, avoiding the memory leak of your code.
return 0 is not required for int main(), but automatically generated.
I'm trying to write a generic template class, but I keep getting this error when I try to implement it:
no matching function for call to type_impl::type_impl()
where type_impl is the type I'm trying to use the class with.
Here's my code:
class BaseClass {
protected:
// Some data
public:
BaseClass(){
// Assign to data
};
};
template <class T>
class HigherClass : BaseClass {
private:
T data;
public:
// Compiler error is here.
HigherClass(){};
// Other functions interacting with data
};
class Bar {
private:
// Some data
public:
Bar(/* Some arguments*/) {
// Assign some arguments to data
};
};
// Implementation
HigherClass<Bar> foo() {
HigherClass<Bar> newFoo;
// Do something to newFoo
return newFoo;
};
The problem is that, since you have provided a nondefault constructor for Bar, the compiler no longer provides a default constructor, and this is required in your code:
HigherClass(){}; // will init data using T()
So provide a default constructor for Bar. For example:
class Bar {
public:
Bar() = default; // or code your own implementation
Bar(/* Some arguments*/) { ... }
};
For example, say I have the following code;
class Foo
{
public:
Foo(int x) : _foo(x)
{
}
private:
int _foo;
protected:
std::string _bar;
};
class Bar : public Foo
{
public:
Bar() : Foo(10), _temp("something"), _bar("something_else")
{
}
private:
std::string _temp;
};
int main()
{
Bar stool;
}
The code doesn't run because _bar is of the class Foo and it doesn't appear to know it exists, so is this not how you would go about doing it? Or would you just have _bar in Foo's constructor? This would work but what if _bar doesn't always have to be assigned something?
Edit: Below is the real code I was using;
Entity::Entity(GameState *state, bool collidable)
:_isLoaded(false), _state(state), alive(true), collidable(collidable), name(entityDetault)
{
}
Entity::Entity(GameState *state, bool collidable, entityName _name)
:_isLoaded(false), _state(state), alive(true), collidable(collidable), name(_name)
{
}
and then the child class would use this constructor;
Player::Player(GameState *state)
: Entity(state,true,entityName::entityPlayer), health(100),bulletSpeed(600),_colour(sf::Color(128,255,86,255))
Does this all look correct now? Slightly better than doing it all in the constructor body.
The member initialiser list in a constructor of class C can only initialise:
direct base classes of C
direct members of C
virtual base classes of C (doesn't come up too often)
The only way to initalise a member of a base class is through a constructor of the base class. Or just forego initialisation and then do an assignment in the body of C's constructor. The latter cannot be used for const members or references, though, and in general does not do the same thing as just initialisation.
You could either move it from the initializer list to the body (if it is not const):
Bar() : Foo(10), _temp("something")
{
_bar = "something_else";
}
or provide a second (maybe protected) contructor for Foo:
class Foo
{
public:
Foo(int x) : _foo(x)
{
}
protected:
Foo(int x,std::string s) : _foo(x), _bar(s)
{
}
private:
int _foo;
protected:
std::string _bar;
};
class Bar : public Foo
{
public:
Bar() : Foo(10,"something_else"), _temp("something")
{
}
private:
std::string _temp;
};
You need to initialize the base class before you can access it. If you want to initialize member variable in the base class, you have to do it via call to base class constructor in which will initialize it's members.
You may put _bar in Foo's constructor's initialization list. If _bar does not always need be to assigned something, you can use default value.
class Foo
{
public:
Foo(int x):_foo(x)
{
}
protected:
Foo(int x, string s) : _foo(x),_bar(s)
{
}
private:
int _foo;
protected:
std::string _bar;
};
class Bar : public Foo
{
public:
Bar() : Foo(10,"something else"), _temp("something")
{
}
private:
std::string _temp;
};