Reputation: 1939
Apparently today, MSVC is trying its best to convince me to switch to clang. But I won't give up. Earlier I asked this question wondering how to declare std::make_unique
as a friend
of my class.
I got a pretty good answer on my simple scenario and indeed when I tried it with clang on wandbox it compiled just fine.
So I go happy back to Visual Studio 2013 to continue coding. A part of my code is this:
// other includes
#include <string>
#include <memory>
template <typename Loader, typename Painter, typename MeshT>
class Model
{
public:
friend std::unique_ptr<Model> std::make_unique<Model>(
const std::string&,
const std::shared_ptr<Loader>&,
const std::shared_ptr<Painter>&);
// Named constructor
static std::unique_ptr<Model> CreateModel(
const std::string& filepath,
const std::shared_ptr<Loader>& loader,
const std::shared_ptr<Painter>& painter)
{
// In case of error longer than the Lord of the Rings trilogy, use the
// line below instead of std::make_unique
//return std::unique_ptr<Model>(new Model(filepath, loader, painter));
return std::make_unique<Model>(filepath, loader, painter);
}
// ...
protected:
// Constructor
Model(
const std::string& filepath,
const std::shared_ptr<Loader>& loader,
const std::shared_ptr<Painter>& painter)
: mFilepath(filepath)
, mLoader(loader)
, mPainter(painter)
{
}
// ...
};
Okay, to be honest I didn't expect to get it right on the first time but I was confident I could make some sense out of the error message:
1>d:\code\c++\projects\active\elesword\src\Model/Model.hpp(28): error C2063: 'std::make_unique' : not a function
1> ..\..\src\Main.cpp(151) : see reference to class template instantiation 'Model<AssimpLoader,AssimpPainter,AssimpMesh>' being compiled
Apparently, MSVC doesn't think that the std::make_unique
function is..well..a function.
The worst part is that I am tired and I got this feeling that I am missing something very very very (...) obvious. Can anyone help me unstuck?
Also, can anyone try this with Visual Studio 2015? Just out of curiosity..
Note: I know I could (and probably should) just use return std::unique_ptr<Model>(new Model(filepath, loader, painter));
but it just doesn't feel right.
Upvotes: 5
Views: 976
Reputation: 1270
A problem is that different compilers may implement std::make_unique differently. E.g. friend unique_ptr<T> make_unique(Args&...);
does not work in Visual Studio 17.7.1 nor GCC 13.2. Here are two slightly different friends that work.
#include <iostream>
#include <memory>
class classA {
public:
class classB {
friend classA;
#ifdef _MSC_VER
// Works in Visual Studio 17.7.1 /std:c++20
template<class _Ty, class... _Types, std::enable_if_t<!std::is_array_v<_Ty>, int> >
friend std::unique_ptr<_Ty> std::make_unique(_Types&&...);
#else
// Works in GCC 13.2. -std=c++20
template<typename _Tp, typename... _Args>
friend std::__detail::__unique_ptr_t<_Tp> std::make_unique(_Args&&...);
#endif
public:
classB() {}
private:
classB(const std::string& str) : mVal(str) {}
std::string mVal;
};
std::unique_ptr<classB> make_classB() {
return std::make_unique<classB>("Hello");
}
};
int main() {
classA a;
auto b = a.make_classB();
}
Regards
Upvotes: 0
Reputation: 47962
Trying to friend std functions puts you in dangerous territory because you're making assumptions about their implementations that aren't guaranteed by the standard. For example, you want std::make_unique to be a friend so that it can access your protected constructor, but what if std::make_unique's implementation delegates this to some other secret function? What you'd need then is to befriend that secret function, but it's secret, so you can't.
Other complications: Some forms of std::make_unique aren't exactly specified by the standard (though I don't think that applies to this exact example). Old versions of VC++ used macro magic to simulate variadic templates before the compiler had full support for variadic templates, so while there is a std::make_unqiue, it might not have the actual signature you expect.
Upvotes: 9