передача в функцию вызова функции в качестве аргумента без временного переменного
Этот код работает:
Nmsp::Base* pBase = new Nmsp::Derived;
*pBase = Nmsp::Derived{}.SetName("New Name");
nmsp.Operate( *pBase );
Но нужно вызвать функцию таким образом:
nmsp.Operate( Nmsp::Derived{}.SetName( "New Name") );
В Visual Studio такой код компилируется, но ведет себя не очень (UB), хотя никаких ошибок не выдает. В wandbox при определенных настройках получаю "pure virtual method called" Понятно, что здесь и есть ошибка (которая в VS "скрыто"), но как исправить - не знаю.
При использовании умных указателей уже и VS жалуется на "создание абстрактного объекта".
Весь код привожу ниже:
#include <iostream>
namespace Nmsp
{
class Base
{
public:
virtual Base& SetName(const std::string& c) = 0;
virtual ~Base() = default;
virtual void Func(std::ostream& out) const = 0;
double param = 1;
};
class Derived : public Base
{
public:
Derived()
{
std::cout << "Derived Ctor" << ", name=" << name << std::endl;
}
Base& SetName(const std::string& c) override
{
name = c;
param = 2;
std::cout << "SetName" << ", New name=" << name << std::endl;
std::cout << "SetName" << ", New param=" << param << std::endl;
return *this;
}
void Func(std::ostream& out) const override
{
out << "Derived param = " << param << std::endl;
out << "Derived caption = " << name << std::endl;
}
~Derived()
{
std::cout << "Derived Dtor" << ", name=" << name << std::endl;
}
std::string name = "Default Name";
};
class Holder //: public Base
{
public:
~Holder()
{
if (basePtr)
delete basePtr;
}
template <typename T>
void Operate(T& bPtr)
{
basePtr = &bPtr;
//basePtr = std::make_unique<T>(std::move(bPtr));
}
void Go(std::ostream& out)
{
basePtr->Func(out);
}
private:
Base* basePtr = nullptr;
// std::unique_ptr<Base> basePtr;
};
}
int main()
{
Nmsp::Holder nmsp;
Nmsp::Base* pBase = new Nmsp::Derived;
*pBase = Nmsp::Derived{}.SetName("New Name");
nmsp.Operate( *pBase );
std::cout << "before go" << std::endl;
// Need to call the function Operate() in this way
//nmsp.Operate( Nmsp::Derived{}.SetName( "New Name") );
nmsp.Go( std::cout );
return 0;
}
Ответы (2 шт):
Так вроде пашет :
nmsp.Operate( (new Nmsp::Derived{})->SetName( "New Name") );
Второй подход :
Чтобы принимал только временный аргумент, пишу move-конструктор. Функцию принимающую временный объект. И получается так :
Derived(Derived && d):name(std::move(d.name))
{
}
void Operate(Nmsp::Derived && bPtr)
{
basePtr = new Nmsp::Derived{std::move(bPtr)};
}
nmsp.Operate(
static_cast<Nmsp::Derived && >(
Nmsp::Derived{}.SetName( "New Name")));
То есть вручную указываю, что объект временный.
Третий подход :
Функция Operate будет всегда создавать новый объект. Пишем виртуальный конструктор копирования.
class Base {
virtual Base & Copy ( ) const = 0 ;
} ;
class Derived {
virtual Base & Copy ( ) const {
return * new Derived (*this);
}
Derived(Derived const & d):name(d.name)
{
}
} ;
class Holder {
template <typename T>
void Operate(T const & bPtr)
{
basePtr = & bPtr.Copy ( ) ;
}
};
И уже точно как вы хотели :
nmsp.Operate(Nmsp::Derived{}.SetName( "New Name"));
Достаточно перестать смешивать RAII и сырые указатели:
#include <iostream>
#include <string>
#include <memory>
#include <utility>
namespace Nmsp
{
class Holder //: public Base
{
public:
~Holder()
{
}
void Operate(::std::unique_ptr<Base> p_base)
{
basePtr = std::move(p_base);
}
void Go(std::ostream& out)
{
basePtr->Func(out);
}
private:
::std::unique_ptr<Base> basePtr;
};
}
int main()
{
Nmsp::Holder nmsp;
auto p_derived{::std::make_unique<Nmsp::Derived>()};
p_derived->SetName("New Name");
nmsp.Operate(::std::unique_ptr<Nmsp::Base>{::std::move(p_derived)});
std::cout << "before go" << std::endl;
nmsp.Go( std::cout );
return 0;
}